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
======================
これは図のスクリーンショットです
ユーザーのDisplayName
とUserTotalMarks
を返し、この結果の説明を注文して、最大の結果を持つユーザーが一番上にくるようにします。
この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
また、実行計画から、TOP
とORDER BY
がSQL Serverによって完全に無視され、最適化されていることがわかります。
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
で並べ替えられた結果を表示するために並べ替えが追加されます(少額の費用なしでご注意ください)。
ですから、話の教訓は、ビューにORDER BYを入れないでください。それらを参照するクエリにORDER BYを入れてください。そして、ソートが高価な場合、それをサポートするためにインデックスを追加/変更することを検討してください。
を使用してビューを強制的に成功させることができました
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は、ビューに対する追加の結合またはフィルターがサイズを縮小した場合でも、完全なビューを強制的に評価するためです!
副作用がなく、正当な理由でビュー内で順序付けを強制する良い方法はありません。
この投稿のコメントの1つが、ストアドプロシージャを使用してデータを返すことを示唆しているように...私はそれが最良の答えだと思います。私の場合、View
を記述してクエリロジックと結合をカプセル化し、Stored Proc
を記述してソートされたデータを返します。また、データ。
次に、ビューをクエリするオプションを選択する必要があります。これにより、データをさらに操作できます。または、ストアドプロシージャを実行するオプションがあります。これは、より高速でより正確な出力です。
データを照会するためのSTORED PROC実行
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