web-dev-qa-db-ja.com

ORDER BY句を使用してビューを作成する

ORDER BY句を使用してビューを作成しようとしています。 SQL Server 2012 SP1で正常に作成しましたが、SQL Server 2008 R2で再作成しようとすると、次のエラーが表示されます。

メッセージ102、レベル15、状態1、プロシージャTopUsers、行11
「OFFSET」付近の構文が正しくありません。

ビューを作成するコードは

CREATE View [dbo].[TopUsersTest] 
as 
select 
u.[DisplayName]  , sum(a.AnswerMark) as Marks
From Users_Questions us inner join [dbo].[Users] u
on u.[UserID] = us.[UserID] 
inner join [dbo].[Answers] a
on a.[AnswerID] = us.[AnswerID]
group by [DisplayName] 
order by Marks desc
OFFSET 0 ROWS

======================

これは図のスクリーンショットです 

ユーザーのDisplayNameUserTotalMarksを返し、この結果の説明を注文して、最大の結果を持つユーザーが一番上にくるようにします。

37
El Sa7eR

このORDER BYが何を達成していると思いますか?あなたがdo場合でも、ORDER BYを正当な方法で(たとえば、TOP句を追加することにより)ビューに入れます。ビューから選択するだけです。例えばSELECT * FROM dbo.TopUsersTest;句がORDER BY句なしの場合、SQL Serverは最も効率的な方法で行を自由に返すことができますが、これは必ずしも期待する順序と一致するとは限りません。これは、結果のソートと、TOPに含める行の指定という2つの目的を果たすという点で、ORDER BYがオーバーロードされているためです。この場合、TOPが常に勝ちます(データのスキャンに選択されたインデックスによっては、注文が期待どおりに機能していることがわかりますが、これは単なる偶然です)。

目的を達成するために、ビュー自体のコードではなく、ビューからデータをプルするクエリにORDER BY句を追加する必要があります。

したがって、ビューコードは次のようになります。

CREATE VIEW [dbo].[TopUsersTest] 
AS 
  SELECT 
    u.[DisplayName], SUM(a.AnswerMark) AS Marks
  FROM
    dbo.Users_Questions AS uq
    INNER JOIN [dbo].[Users] AS u
      ON u.[UserID] = us.[UserID] 
    INNER JOIN [dbo].[Answers] AS a
      ON a.[AnswerID] = uq.[AnswerID]
    GROUP BY u.[DisplayName];

ORDER BYは無意味なので、含めることもできません。


説明のために、AdventureWorks2012を使用して、以下に例を示します。

CREATE VIEW dbo.SillyView
AS
  SELECT TOP 100 PERCENT 
    SalesOrderID, OrderDate, CustomerID , AccountNumber, TotalDue
  FROM Sales.SalesOrderHeader
  ORDER BY CustomerID;
GO

SELECT SalesOrderID, OrderDate, CustomerID, AccountNumber, TotalDue
FROM dbo.SillyView;

結果:

SalesOrderID   OrderDate   CustomerID   AccountNumber   TotalDue
------------   ----------  ----------   --------------  ----------
43659          2005-07-01  29825        10-4020-000676  23153.2339
43660          2005-07-01  29672        10-4020-000117  1457.3288
43661          2005-07-01  29734        10-4020-000442  36865.8012
43662          2005-07-01  29994        10-4020-000227  32474.9324
43663          2005-07-01  29565        10-4020-000510  472.3108

また、実行計画から、TOPORDER BYがSQL Serverによって完全に無視され、最適化されていることがわかります。

enter image description here

TOP演算子はまったくなく、ソートもありません。 SQL Serverはそれらを完全に最適化しました。

ここで、ビューをORDER BY SalesIDと言うように変更すると、偶然、ビューが示す順序を取得しますが、前述のように偶然です。

ただし、必要なORDER BYを実行するように外部クエリを変更した場合:

SELECT SalesOrderID, OrderDate, CustomerID, AccountNumber, TotalDue
FROM dbo.SillyView
ORDER BY CustomerID;

希望する方法で結果を並べ替えます:

SalesOrderID   OrderDate   CustomerID   AccountNumber   TotalDue
------------   ----------  ----------   --------------  ----------
43793          2005-07-22  11000        10-4030-011000  3756.989
51522          2007-07-22  11000        10-4030-011000  2587.8769
57418          2007-11-04  11000        10-4030-011000  2770.2682
51493          2007-07-20  11001        10-4030-011001  2674.0227
43767          2005-07-18  11001        10-4030-011001  3729.364

また、プランはビューのTOP/ORDER BYを最適化して削除しますが、CustomerIDで並べ替えられた結果を表示するために並べ替えが追加されます(少額の費用なしでご注意ください)。

enter image description here

ですから、話の教訓は、ビューにORDER BYを入れないでください。それらを参照するクエリにORDER BYを入れてください。そして、ソートが高価な場合、それをサポートするためにインデックスを追加/変更することを検討してください。

72
Aaron Bertrand

を使用してビューを強制的に成功させることができました

SELECT TOP 9999999 ... ORDER BY something

残念ながら、SELECT TOP 100 PERCENTを使用しても問題 here が原因で機能しません。

SQL 2012から、OFFSETを使用してビューとサブクエリの順序を強制できます。

SELECT      C.CustomerID,
            C.CustomerName,
            C.CustomerAge
FROM        dbo.Customer C
ORDER BY    CustomerAge OFFSET 0 ROWS;

警告:これは小さなリストでのみ使用する必要があります。OFFSETは、ビューに対する追加の結合またはフィルターがサイズを縮小した場合でも、完全なビューを強制的に評価するためです!

副作用がなく、正当な理由でビュー内で順序付けを強制する良い方法はありません。

2
Tom Deloford

この投稿のコメントの1つが、ストアドプロシージャを使用してデータを返すことを示唆しているように...私はそれが最良の答えだと思います。私の場合、Viewを記述してクエリロジックと結合をカプセル化し、Stored Procを記述してソートされたデータを返します。また、データ。

次に、ビューをクエリするオプションを選択する必要があります。これにより、データをさらに操作できます。または、ストアドプロシージャを実行するオプションがあります。これは、より高速でより正確な出力です。

データを照会するためのSTORED PROC実行

exec [olap].[uspUsageStatsLogSessionsRollup]

VIEW定義

USE [DBA]
GO

/****** Object:  View [olap].[vwUsageStatsLogSessionsRollup]    Script Date: 2/19/2019 10:10:06 AM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO


--USE DBA
-- select * from olap.UsageStatsLog_GCOP039 where CubeCommand='[ORDER_HISTORY]'
;

ALTER VIEW [olap].[vwUsageStatsLogSessionsRollup] as
(
    SELECT --*
        t1.UsageStatsLogDate
        , COALESCE(CAST(t1.UsageStatsLogDate AS nvarchar(100)), 'TOTAL- DATES:') AS UsageStatsLogDate_Totals
        , t1.ADUserNameDisplayNEW
        , COALESCE(t1.ADUserNameDisplayNEW, 'TOTAL- USERS:') AS ADUserNameDisplay_Totals
        , t1.CubeCommandNEW
        , COALESCE(t1.CubeCommandNEW, 'TOTAL- CUBES:') AS CubeCommand_Totals
        , t1.SessionsCount
        , t1.UsersCount
        , t1.CubesCount
    FROM
    (
        select 
            CAST(olapUSL.UsageStatsLogTime as date) as UsageStatsLogDate
            , olapUSL.ADUserNameDisplayNEW
            , olapUSL.CubeCommandNEW
            , count(*) SessionsCount
            , count(distinct olapUSL.ADUserNameDisplayNEW) UsersCount
            , count(distinct olapUSL.CubeCommandNEW) CubesCount
        from 
            olap.vwUsageStatsLog olapUSL
        where CubeCommandNEW != '[]'
        GROUP BY CUBE(CAST(olapUSL.UsageStatsLogTime as date), olapUSL.ADUserNameDisplayNEW, olapUSL.CubeCommandNEW )
            ----GROUP BY 
            ------GROUP BY GROUPING SETS
            --------GROUP BY ROLLUP
    ) t1

    --ORDER BY
    --  t1.UsageStatsLogDate DESC
    --  , t1.ADUserNameDisplayNEW
    --  , t1.CubeCommandNEW
)
;


GO

STORED PROC Definition

USE [DBA]
GO

/****** Object:  StoredProcedure [olap].[uspUsageStatsLogSessionsRollup]    Script Date: 2/19/2019 9:39:31 AM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO


-- =============================================
-- Author:      BRIAN LOFTON
-- Create date: 2/19/2019
-- Description: This proceedured returns data from a view with sorted results and an optional date range filter.
-- =============================================
ALTER PROCEDURE [olap].[uspUsageStatsLogSessionsRollup]
    -- Add the parameters for the stored procedure here
    @paramStartDate date = NULL,
    @paramEndDate date = NULL,
    @paramDateTotalExcluded as int = 0,
    @paramUserTotalExcluded as int = 0,
    @paramCubeTotalExcluded as int = 0
AS

BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from interfering with SELECT statements.
    SET NOCOUNT ON;

    DECLARE @varStartDate as date 
        = CASE  
            WHEN @paramStartDate IS NULL THEN '1900-01-01' 
            ELSE @paramStartDate 
        END
    DECLARE @varEndDate as date 
        = CASE  
            WHEN @paramEndDate IS NULL THEN '2100-01-01' 
            ELSE @paramStartDate 
        END

    -- Return Data from this statement
    SELECT 
        t1.UsageStatsLogDate_Totals
        , t1.ADUserNameDisplay_Totals
        , t1.CubeCommand_Totals
        , t1.SessionsCount
        , t1.UsersCount
        , t1.CubesCount
        -- Fields with NULL in the totals
            --  , t1.CubeCommandNEW
            --  , t1.ADUserNameDisplayNEW
            --  , t1.UsageStatsLogDate
    FROM 
        olap.vwUsageStatsLogSessionsRollup t1
    WHERE

        (
            --t1.UsageStatsLogDate BETWEEN @varStartDate AND @varEndDate
            t1.UsageStatsLogDate BETWEEN '1900-01-01' AND '2100-01-01'
            OR t1.UsageStatsLogDate IS NULL
        )
        AND
        (
            @paramDateTotalExcluded=0
            OR (@paramDateTotalExcluded=1 AND UsageStatsLogDate_Totals NOT LIKE '%TOTAL-%')
        )
        AND
        (
            @paramDateTotalExcluded=0
            OR (@paramUserTotalExcluded=1 AND ADUserNameDisplay_Totals NOT LIKE '%TOTAL-%')
        )
        AND
        (
            @paramCubeTotalExcluded=0
            OR (@paramCubeTotalExcluded=1 AND CubeCommand_Totals NOT LIKE '%TOTAL-%')
        )
    ORDER BY
            t1.UsageStatsLogDate DESC
            , t1.ADUserNameDisplayNEW
            , t1.CubeCommandNEW

END


GO

セレクトでトップ100パーセントを使用するだけです。

     CREATE VIEW [schema].[VIEWNAME] (
         [COLUMN1],
         [COLUMN2],
         [COLUMN3],
         [COLUMN4])
     AS 
        SELECT TOP 100 PERCENT 
         alias.[COLUMN1],
         alias.[COLUMN2],
         alias.[COLUMN3],
         alias.[COLUMN4]
        FROM 
           [schema].[TABLENAME] AS alias
          ORDER BY alias.COLUMN1
     GO
0
Spektakulatius