SQL Row_Number () -Funktion in Where-Klausel


90

Ich fand eine Frage beantwortet mit dem Row_Number() Funktion in der where-Klausel . Als ich eine Abfrage versuchte, wurde folgende Fehlermeldung angezeigt:

"Meldung 4108, Ebene 15, Status 1, Zeile 1 Fensterfunktionen können nur in den Klauseln SELECT oder ORDER BY angezeigt werden."

Hier ist die Abfrage, die ich versucht habe. Wenn jemand weiß, wie man das löst, lass es mich wissen.

SELECT employee_id 
FROM V_EMPLOYEE 
WHERE row_number() OVER ( ORDER BY employee_id ) > 0 
ORDER BY Employee_ID

9
ROW_NUMBER() OVER (ORDER BY employee_id) > 0wird immer zuTRUE
Quassnoi

3
Ja, das ist richtig. Ich mache mir keine Sorgen um den Zustand, den ich jederzeit ändern kann. Ich möchte, dass die Abfrage zuerst funktioniert und dann daran denkt, die Anzahl zwischen 500 und 800 zu halten ... danke

2
@ Joseph: Warum versuchen Sie, die Verwendung eines CTE zu vermeiden?
OMG Ponys

1
@rexem - Ich bin kein Experte für SQL Server. Ich versuche, einem Team in einem großen Projekt zu helfen, in dem es viele Probleme mit der Leistung hat. Sie verwenden UDFs und CTEs. In einer der Tabellen befinden sich nur 5000 Datensätze. Wenn 5 Benutzer auf eine Suche zugreifen, dauert das Abrufen mehr als eine Minute. Irgendwann schlägt es fehl und es tritt eine Zeitüberschreitung auf. Daher versuche ich, CTE und UDFs zu vermeiden und eine einfache SQL-Abfrage zu erstellen, mit der die Leistungsprobleme behoben werden können.

1
Hallo zusammen, bitte sehen Sie den Link, den ich unten gepostet habe, welche Antworten mit row_number () auf andere Weise beantwortet werden. Kann jemand meine erste Anfrage mit der im Link vergleichen? Schätzen Sie die Hilfe ..

Antworten:


91

Um dieses Problem zu umgehen, schließen Sie Ihre select-Anweisung in einen CTE ein. Anschließend können Sie den CTE abfragen und die Ergebnisse der Fensterfunktion in der where-Klausel verwenden.

WITH MyCte AS 
(
    select   employee_id,
             RowNum = row_number() OVER ( order by employee_id )
    from     V_EMPLOYEE 
    ORDER BY Employee_ID
)
SELECT  employee_id
FROM    MyCte
WHERE   RowNum > 0

7
Ich versuche, CTE zu vermeiden. Das ist der schlimmste Fall, den ich suche. danke

3
Es kann schneller laufen, wenn Sie eine Unterabfrage anstelle eines CTE verwenden. Ich habe in einigen Fällen eine um den Faktor 1,5 bessere Leistung gesehen
Brian Webster,

3
Es sollte auch TOP im CTE SELECT enthalten sein, sonst führt SQL 2008 Server die Abfrage aufgrund von ORDER BY nicht aus (was nicht unterstützt wird, wenn TOP nicht verwendet wird)
Muflix

2
Ich verwende SQL2005 (ugh) - Ich kann die Verwendung von "TOP" vermeiden, indem ich "ORDER BY" nach dem FROM ablege. Es ist sowieso überflüssig mit dem (Order By) nach dem OVER.
Joe B

Ich wünschte, es gibt eine Möglichkeit, ROW_NUMBER()in WHEREKlausel ohne CTE zu verwenden :(
Jalal

61
SELECT  employee_id
FROM    (
        SELECT  employee_id, ROW_NUMBER() OVER (ORDER BY employee_id) AS rn
        FROM    V_EMPLOYEE
        ) q
WHERE   rn > 0
ORDER BY
        Employee_ID

Beachten Sie, dass dieser Filter redundant ist: Er ROW_NUMBER()beginnt mit 1und ist immer größer als 0.


2
@ DavideChicco.it: In SQL Server benötigen abgeleitete Tabellen einen Alias ​​(ich hätte AS qstattdessen schreiben sollen , aber das würde auch funktionieren).
Quassnoi

2
Die Lesbarkeit ist ein Schwerpunkt, den ich bei der Benennung von Aliasnamen habe. Sie könnten rn als RowNumber und q als DerivedTable und die where-Klausel als where DerivedTable.RowNumber> 0 schreiben. Meiner Meinung nach wird dies in 6 Monaten weitaus weniger verwirrend sein, wenn der Code in Ihrem Kopf nicht frisch ist.
Edward Comeau

2
@EdwardComeau: rnist heutzutage ein allgemein anerkanntes Akronym für Zeilennummer. Geben Sie "row_number over as ..." in die Google-Suchzeichenfolge ein und sehen Sie, was Ihnen vorgeschlagen wird.
Quassnoi

3
@Quassnoi, Lesbarkeit ist der Schlüssel zu einer guten Codierung, und der kognitive Aufwand für die Übersetzung von rn (oder anderen abgekürzten Aliasen) summiert sich für Sie und die Personen, die Ihren Code verwalten. NB, Microsoft erster Treffer, SELECT ROW_NUMBER () OVER (ORDER BY SalesYTD DESC) AS Row, ... Ich bin auch noch nie auf rn gestoßen, sodass Ihr Kilometerstand in "universal" variieren kann.
Edward Comeau

1
@Quassnoi und zweiter Treffer, SO-Artikel - stackoverflow.com/questions/961007/how-do-i-use-row-number mehrere Variationen und nicht rn ;-)
Edward Comeau

32
Select * from 
(
    Select ROW_NUMBER() OVER ( order by Id) as 'Row_Number', * 
    from tbl_Contact_Us
) as tbl
Where tbl.Row_Number = 5

19

Ich denke du willst so etwas:

SELECT employee_id 
FROM  (SELECT employee_id, row_number() 
       OVER (order by employee_id) AS 'rownumber' 
       FROM V_EMPLOYEE) TableExpressionsMustHaveAnAliasForDumbReasons
WHERE rownumber > 0

4
Erstellen Sie einen Alias ​​für die Tabelle, wenn die obige Abfrage für Sie nicht funktioniert. Ändern Sie die vorletzte Zeile, indem From V_EMPLOYEE) ASie A als Alias ​​hinzufügen.
Hammad Khan

7

Als Antwort auf Kommentare zur Antwort von rexem, ob eine Inline-Ansicht oder ein CTE schneller wäre, habe ich die Abfragen neu gefasst, um eine Tabelle zu verwenden, die mir und allen zur Verfügung stand: sys.objects.

WITH object_rows AS (
    SELECT object_id, 
        ROW_NUMBER() OVER ( ORDER BY object_id) RN
    FROM sys.objects)
SELECT object_id
FROM object_rows
WHERE RN > 1

SELECT object_id
FROM (SELECT object_id, 
        ROW_NUMBER() OVER ( ORDER BY object_id) RN
    FROM sys.objects) T
WHERE RN > 1

Die erstellten Abfragepläne waren genau gleich. Ich würde in allen Fällen erwarten, dass der Abfrageoptimierer denselben Plan erstellt, zumindest beim einfachen Ersetzen von CTE durch Inline-Ansicht oder umgekehrt.

Versuchen Sie natürlich Ihre eigenen Abfragen auf Ihrem eigenen System, um festzustellen, ob es einen Unterschied gibt.

Außerdem ist row_number()in der where-Klausel ein häufiger Fehler bei den Antworten zum Stapelüberlauf enthalten. Logicaly row_number()ist erst verfügbar, wenn die select-Klausel verarbeitet wurde. Die Leute vergessen das und wenn sie antworten, ohne die Antwort zu testen, ist die Antwort manchmal falsch. (Eine Anklage, an der ich mich schuldig gemacht habe.)


1
Danke Shannon. Welche Version von SQL Server haben Sie verwendet?
OMG Ponys

1
Das heißt, die Antwort in diesem Link ist falsch? Aber die Person, die die Frage gestellt hat, stimmte zu, dass sie funktioniert. Überraschend. :-)

2
@Joseph, aber wenn Sie sich eine andere Antwort ansehen, die vom OP in der verknüpften Frage gepostet wurde, werden Sie sehen, dass er auf eine Version des Codes verweist, die nicht mit der akzeptierten Antwort übereinstimmt. Ich weiß nicht, warum er die Antwort akzeptiert hat, obwohl sie nicht wie eingegeben ausgeführt werden würde. Vielleicht wurde es irgendwann nach seiner Annahme bearbeitet, vielleicht war es genug, um ihn zum Laufen zu bringen, auch ohne völlig korrekt zu sein.
Shannon Severance

1
@Rexem: Sowohl SQL Server 2005 als auch SQL Server 2008. Frühere Versionen unterstützen keine CTEs oder ROW_NUMBER ()
Shannon Severance

5

Ich bin der Meinung, dass alle Antworten, die die Verwendung eines CTE oder einer Unterabfrage zeigen, ausreichende Korrekturen dafür sind, aber ich sehe niemanden, der auf den Grund geht, warum OP ein Problem hat. Der Grund, warum das, was OP vorgeschlagen hat, nicht funktioniert, liegt in der logischen Reihenfolge der Abfrageverarbeitung:

  1. VON
  2. AUF
  3. BEITRETEN
  4. WO
  5. GRUPPIERE NACH
  6. MIT WÜRFEL / ROLLUP
  7. HABEN
  8. WÄHLEN
  9. UNTERSCHEIDLICH
  10. SORTIEREN NACH
  11. OBEN
  12. OFFSET / FETCH

Ich glaube, dass dies einen großen Beitrag zur Antwort leistet, da es erklärt, warum Probleme wie dieses auftreten. WHEREwird immer verarbeitet, bevor SELECTein CTE oder eine Unterabfrage für viele Funktionen erforderlich wird. Sie werden dies häufig in SQL Server sehen.


4

Verwenden von CTE (SQL Server 2005+):

WITH employee_rows AS (
  SELECT t.employee_id,
         ROW_NUMBER() OVER ( ORDER BY t.employee_id ) 'rownum'
    FROM V_EMPLOYEE t)
SELECT er.employee_id
  FROM employee_rows er
 WHERE er.rownum > 1

Verwenden der Inline-Ansicht / Nicht-CTE-äquivalente Alternative:

SELECT er.employee_id
  FROM (SELECT t.employee_id,
               ROW_NUMBER() OVER ( ORDER BY t.employee_id ) 'rownum'
          FROM V_EMPLOYEE t) er
 WHERE er.rownum > 1

1
Welches ist leistungsfähiger? CTE oder Unterabfrage verwenden? danke

1
Siehe Shannons Antwort - in seinem Test sind sie gleich.
OMG Ponys

6
Nein, es ist nicht schneller. In SQL Server, CTE's und Inline-Ansichten sind dasselbe und haben dieselbe Leistung. Wenn in a nicht deterministische Funktionen verwendet werden, wird diese bei CTEjedem Aufruf neu bewertet. Man muss schmutzige Tricks anwenden, um die Materialisierung von a zu erzwingen CTE. Siehe diese Artikel in meinem Blog: explainextended.com/2009/07/28/… explainextended.com/2009/05/28/generating-xml-in-subqueries
Quassnoi

2

basierend auf der Antwort von OP auf die Frage:

Bitte sehen Sie diesen Link. Es hat eine andere Lösung, die für die Person, die die Frage gestellt hat, funktioniert. Ich versuche eine solche Lösung zu finden.

Paginierte Abfrage durch Sortieren nach verschiedenen Spalten mit ROW_NUMBER () OVER () in SQL Server 2005

~ Joseph

"Methode 1" entspricht der Abfrage des OP aus der verknüpften Frage, und "Methode 2" entspricht der Abfrage aus der ausgewählten Antwort. Sie mussten sich den in dieser Antwort verknüpften Code ansehen, um zu sehen, was wirklich vor sich ging, da der Code in der ausgewählten Antwort geändert wurde, damit er funktioniert. Versuche dies:

DECLARE @YourTable table (RowID int not null primary key identity, Value1 int, Value2 int, value3 int)
SET NOCOUNT ON
INSERT INTO @YourTable VALUES (1,1,1)
INSERT INTO @YourTable VALUES (1,1,2)
INSERT INTO @YourTable VALUES (1,1,3)
INSERT INTO @YourTable VALUES (1,2,1)
INSERT INTO @YourTable VALUES (1,2,2)
INSERT INTO @YourTable VALUES (1,2,3)
INSERT INTO @YourTable VALUES (1,3,1)
INSERT INTO @YourTable VALUES (1,3,2)
INSERT INTO @YourTable VALUES (1,3,3)
INSERT INTO @YourTable VALUES (2,1,1)
INSERT INTO @YourTable VALUES (2,1,2)
INSERT INTO @YourTable VALUES (2,1,3)
INSERT INTO @YourTable VALUES (2,2,1)
INSERT INTO @YourTable VALUES (2,2,2)
INSERT INTO @YourTable VALUES (2,2,3)
INSERT INTO @YourTable VALUES (2,3,1)
INSERT INTO @YourTable VALUES (2,3,2)
INSERT INTO @YourTable VALUES (2,3,3)
INSERT INTO @YourTable VALUES (3,1,1)
INSERT INTO @YourTable VALUES (3,1,2)
INSERT INTO @YourTable VALUES (3,1,3)
INSERT INTO @YourTable VALUES (3,2,1)
INSERT INTO @YourTable VALUES (3,2,2)
INSERT INTO @YourTable VALUES (3,2,3)
INSERT INTO @YourTable VALUES (3,3,1)
INSERT INTO @YourTable VALUES (3,3,2)
INSERT INTO @YourTable VALUES (3,3,3)
SET NOCOUNT OFF

DECLARE @PageNumber     int
DECLARE @PageSize       int
DECLARE @SortBy         int

SET @PageNumber=3
SET @PageSize=5
SET @SortBy=1


--SELECT * FROM @YourTable

--Method 1
;WITH PaginatedYourTable AS (
SELECT
    RowID,Value1,Value2,Value3
        ,CASE @SortBy
             WHEN  1 THEN ROW_NUMBER() OVER (ORDER BY Value1 ASC)
             WHEN  2 THEN ROW_NUMBER() OVER (ORDER BY Value2 ASC)
             WHEN  3 THEN ROW_NUMBER() OVER (ORDER BY Value3 ASC)
             WHEN -1 THEN ROW_NUMBER() OVER (ORDER BY Value1 DESC)
             WHEN -2 THEN ROW_NUMBER() OVER (ORDER BY Value2 DESC)
             WHEN -3 THEN ROW_NUMBER() OVER (ORDER BY Value3 DESC)
         END AS RowNumber
    FROM @YourTable
    --WHERE
)
SELECT
    RowID,Value1,Value2,Value3,RowNumber
        ,@PageNumber AS PageNumber, @PageSize AS PageSize, @SortBy AS SortBy
    FROM PaginatedYourTable
    WHERE RowNumber>=(@PageNumber-1)*@PageSize AND RowNumber<=(@PageNumber*@PageSize)-1
    ORDER BY RowNumber



--------------------------------------------
--Method 2
;WITH PaginatedYourTable AS (
SELECT
    RowID,Value1,Value2,Value3
        ,ROW_NUMBER() OVER
         (
             ORDER BY
                 CASE @SortBy
                     WHEN  1 THEN Value1
                     WHEN  2 THEN Value2
                     WHEN  3 THEN Value3
                 END ASC
                ,CASE @SortBy
                     WHEN -1 THEN Value1
                     WHEN -2 THEN Value2
                     WHEN -3 THEN Value3
                 END DESC
         ) RowNumber
    FROM @YourTable
    --WHERE  more conditions here
)
SELECT
    RowID,Value1,Value2,Value3,RowNumber
        ,@PageNumber AS PageNumber, @PageSize AS PageSize, @SortBy AS SortBy
    FROM PaginatedYourTable
    WHERE 
        RowNumber>=(@PageNumber-1)*@PageSize AND RowNumber<=(@PageNumber*@PageSize)-1
        --AND more conditions here
    ORDER BY
        CASE @SortBy
            WHEN  1 THEN Value1
            WHEN  2 THEN Value2
            WHEN  3 THEN Value3
        END ASC
       ,CASE @SortBy
            WHEN -1 THEN Value1
            WHEN -2 THEN Value2
            WHEN -3 THEN Value3
        END DESC

AUSGABE:

RowID  Value1 Value2 Value3 RowNumber  PageNumber  PageSize    SortBy
------ ------ ------ ------ ---------- ----------- ----------- -----------
10     2      1      1      10         3           5           1
11     2      1      2      11         3           5           1
12     2      1      3      12         3           5           1
13     2      2      1      13         3           5           1
14     2      2      2      14         3           5           1

(5 row(s) affected

RowID  Value1 Value2 Value3 RowNumber  PageNumber  PageSize    SortBy
------ ------ ------ ------ ---------- ----------- ----------- -----------
10     2      1      1      10         3           5           1
11     2      1      2      11         3           5           1
12     2      1      3      12         3           5           1
13     2      2      1      13         3           5           1
14     2      2      2      14         3           5           1

(5 row(s) affected)

1
Zu Ihrer Information : Bei Verwendung von SET SHOWPLAN_ALL ON hatte Methode 1 einen TotalSubtreeCost von 0,08424953, während Methode 2 bei 0,02627153 lag. Methode 2 war mehr als dreimal besser.
KM.

1
@rexem, sowohl Methode 1 als auch Methode 2 verwenden CTEs. Die Art und Weise, wie sie Zeilen paginieren und ordnen, ist unterschiedlich. Ich bin nicht sicher, warum sich diese eigentliche Frage so stark von der Frage unterscheidet, auf die das OP verweist (in der Antwort des OP auf diese Frage), aber meine Antwort erstellt einen Arbeitscode basierend auf dem Link, auf den sich das OP bezieht
KM.

1
Danke, ich versuche den alten Beitrag und diese Antworten zu vergleichen. [Ich weiß nicht, wie ich das formatieren soll] Hier ist die Antwort von Tomalak. stackoverflow.com/questions/230058?sort=votes#sort-top Ist das falsch? Wenn er nur die Hälfte der Antwort gepostet hat, wie kann ich dann mit seiner besseren Leistung bei der Abfrage fortfahren? Bitte geben Sie mir etwas mehr Licht, um fortzufahren. Danke

@Joseph, die ausgewählte Antwort in dem von Ihnen angegebenen Link ( stackoverflow.com/questions/230058?sort=votes#sort-top ) unterscheidet sich von dem Arbeitscode, den die Person, die die Frage stellt, als Arbeit in ihrer Antwort angibt : stackoverflow.com/ Fragen / 230058 /… Wenn Sie diese Antwort lesen, sehen Sie einen Link zu ihrem Code: pastebin.com/f26a4b403 und einen Link zu ihrer Version von Tomalaks: pastebin.com/f4db89a8e in meiner Antwort. Ich stelle eine funktionierende Version jeder Version mit zur Verfügung Tabellenvariablen
KM.

2
WITH MyCte AS 
(
    select 
       employee_id,
       RowNum = row_number() OVER (order by employee_id)
    from V_EMPLOYEE 
)
SELECT  employee_id
FROM    MyCte
WHERE   RowNum > 0
ORDER BY employee_id

-1
 select salary from (
 select  Salary, ROW_NUMBER() over (order by Salary desc) rn from Employee 
 ) t where t.rn = 2

3
Willkommen bei Stack Overflow! Während dieses Code-Snippet die Lösung sein kann, hilft das Hinzufügen einer Erklärung wirklich, die Qualität Ihres Beitrags zu verbessern. Denken Sie daran, dass Sie die Frage für Leser in Zukunft beantworten und diese Personen möglicherweise die Gründe für Ihren Codevorschlag nicht kennen.
Johan

Bitte fügen Sie dem Code-Snippet einen Kontext hinzu, um den zukünftigen Lesern zu helfen.
DebanjanB
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.