it-swarm.com.de

Erstellen Sie eine Ansicht mit der ORDER BY-Klausel

Ich versuche, eine Ansicht mit einer ORDER BY-Klausel zu erstellen. Ich habe es erfolgreich in SQL Server 2012 SP1 erstellt, aber wenn ich versuche, es in SQL Server 2008 R2 neu zu erstellen, erhalte ich diese Fehlermeldung: 

Meldung 102, Ebene 15, Status 1, Prozedur TopUsers, Zeile 11
Falsch Syntax in der Nähe von 'OFFSET'.

Der Code zum Erstellen der Ansicht lautet

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

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

Dies ist ein Screenshot des Diagramms

Ich möchte die DisplayName der Benutzer und die UserTotalMarks zurückgeben und dieses Ergebnis absteigend ordnen, damit der Benutzer mit dem größten Ergebnis oben ist.

35
El Sa7eR

Ich bin mir nicht sicher, was Sie davon halten, dass dieser ORDER BY erfüllt ist? Selbst wenn Sie doORDER BY auf eine legale Weise in die Ansicht einfügen (z. B. durch Hinzufügen einer TOP-Klausel), wenn Sie nur aus der Sicht auswählen, z. SELECT * FROM dbo.TopUsersTest; ohne ORDER BY-Klausel kann der SQL Server die Zeilen auf die effizienteste Weise zurückgeben, was nicht unbedingt der erwarteten Reihenfolge entspricht. Dies liegt daran, dass ORDER BY überlastet ist, indem er zwei Zwecke erfüllt: das Sortieren der Ergebnisse und das Bestimmen der Zeilen, die in TOP aufgenommen werden sollen. In diesem Fall gewinnt TOP immer (obwohl abhängig von dem für das Scannen der Daten ausgewählten Index Sie feststellen können, dass Ihre Bestellung wie erwartet funktioniert - dies ist jedoch nur ein Zufall).

Um das zu erreichen, was Sie möchten, müssen Sie die ORDER BY-Klausel zu den Abfragen hinzufügen, die Daten aus der Ansicht abrufen, nicht dem Code der Ansicht selbst.

Ihr Ansichtscode sollte also Folgendes sein:

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];

Der ORDER BY ist bedeutungslos, sollte also nicht einmal einbezogen werden.


Zur Veranschaulichung der Verwendung von AdventureWorks2012 ein Beispiel:

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;

Ergebnisse:

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

Aus dem Ausführungsplan können Sie ersehen, dass TOP und ORDER BY absolut ignoriert und von SQL Server optimiert wurden:

enter image description here

Es gibt überhaupt keinen TOP-Operator und keine Sortierung. SQL Server hat sie vollständig optimiert.

Wenn Sie nun die Ansicht ändern, um ORDER BY SalesID zu sagen, werden Sie zufällig die Reihenfolge erhalten, die die Ansicht angibt, jedoch nur - wie bereits erwähnt - zufällig.

Wenn Sie jedoch Ihre äußere Abfrage ändern, um den gewünschten ORDER BY auszuführen:

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

Sie erhalten die Ergebnisse so, wie Sie möchten:

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

Und der Plan hat die TOP/ORDER BY in der Ansicht immer noch wegoptimiert, aber es wird eine Sortierung hinzugefügt (ohne zuviel Aufwand), um die Ergebnisse nach CustomerID sortiert zu präsentieren:

enter image description here

Also, Moral der Geschichte, ORDER BY nicht in Ansichten setzen. Fügen Sie ORDER BY in die Abfragen ein, die auf sie verweisen. Und wenn die Sortierung teuer ist, können Sie einen Index hinzufügen/ändern, um ihn zu unterstützen.

69
Aaron Bertrand

Ich hatte Erfolg, die Ansicht mit zu bestellen

SELECT TOP 9999999 ... ORDER BY something

Leider funktioniert die Verwendung von SELECT TOP 100 PERCENT aufgrund des Problems hier nicht.

Ab SQL 2012 können Sie mit OFFSET die Bestellung in Ansichten und Unterabfragen erzwingen

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

Achtung: Dies sollte nur für kleine Listen verwendet werden, da OFFSET die Bewertung der gesamten Ansicht erzwingt, auch wenn weitere Verknüpfungen oder Filter in der Ansicht die Größe verringern.

Es gibt keine gute Möglichkeit, die Anordnung in einer Ansicht ohne Nebeneffekt wirklich und aus gutem Grund zu erzwingen.

2
Tom Deloford

Da einer der Kommentare in diesem Beitrag vorschlägt, gespeicherte Prozeduren zu verwenden, um die Daten zurückzugeben ... Ich denke, das ist die beste Antwort. In meinem Fall habe ich eine View geschrieben, um die Abfragelogik und Verknüpfungen zu kapseln. Dann schrieb ich einen Stored Proc, um die Daten sortiert wiederzugeben. Außerdem enthält die Prozedur weitere Erweiterungsfunktionen, z. 

Jetzt müssen Sie die Ansicht abfragen, um die Daten weiter zu bearbeiten. Oder Sie haben die Möglichkeit, das gespeicherte proc auszuführen, was eine schnellere und genauere Ausgabe ermöglicht. 

STORED PROC Ausführung zum Abfragen von Daten

 exec [olap].[uspUsageStatsLogSessionsRollup]

VIEW Definition

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

Verwenden Sie einfach TOP 100 Prozent in der Auswahl:

     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
Code.IT