Funzione SQL Row_Number () nella clausola Where


90

Ho trovato una risposta a una domanda con la Row_Number()funzione nella clausola where. Quando ho provato una query, ho ricevuto il seguente errore:

"Msg 4108, livello 15, stato 1, le funzioni con finestra riga 1 possono essere visualizzate solo nelle clausole SELECT o ORDER BY."

Ecco la query che ho provato. Se qualcuno sa come risolvere questo problema, fammelo sapere.

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) > 0valuterà sempre aTRUE
Quassnoi

3
Sì, è vero. Non sono preoccupato per la condizione, che posso modificare in qualsiasi momento. Voglio che la query funzioni prima, quindi penso di mantenere il numero di telefono tra 500 e 800 ... grazie

2
@ Joseph: Perché stai cercando di evitare di utilizzare un CTE?
OMG Ponies,

1
@rexem - Non sono un esperto di SQL Server. Sto cercando di aiutare una squadra in un grande progetto in cui devono affrontare molti problemi con le prestazioni. Stanno usando UDF e CTE. In una delle tabelle, hanno solo 5000 record e se 5 utenti accedono a una ricerca, il recupero richiede più di un minuto. Qualche volta, fallisce e va in timeout. Quindi, sto cercando di evitare CTE e UDF e sto cercando di elaborare una semplice query SQL che possa risolvere i problemi di prestazioni.

1
Ciao a tutti, per favore guarda il link che ho postato di seguito che risponde usando row_number () in un modo diverso. Qualcuno può confrontare la mia domanda iniziale con quella nel link? Apprezzo l'aiuto ..

Risposte:


91

Per aggirare questo problema, racchiudere l'istruzione select in una CTE, quindi è possibile eseguire una query sulla CTE e utilizzare i risultati della funzione con finestra nella clausola where.

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
Sto cercando di evitare CTE. Questo è il caso peggiore che sto cercando. grazie

3
Potrebbe essere più veloce se si utilizza una sottoquery invece di una CTE. Ho visto prestazioni migliori di un fattore 1,5 in alcuni casi
Brian Webster

3
Dovrebbe esserci anche TOP in CTE SELECT altrimenti SQL 2008 Server non eseguirà la query a causa di ORDER BY (che non è supportato a meno che non venga utilizzato TOP)
Muflix

2
Sto usando SQL2005 (ugh) - posso evitare l'uso di "TOP", eliminando "ORDER BY" dopo FROM. È comunque ridondante con (Order By) dopo OVER.
Joe B

Ero che desiderano ci sia un modo per utilizzare ROW_NUMBER()in WHEREclausola senza CTE :(
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

Nota che questo filtro è ridondante: ROW_NUMBER()inizia da 1ed è sempre maggiore di 0.


2
@ DavideChicco.it: in SQL Server, le tabelle derivate richiedono un alias (avrei dovuto scrivere AS qinvece, ma anche questo avrebbe funzionato).
Quassnoi

2
La leggibilità è un obiettivo che ho quando nomino gli alias. Potresti scrivere rn come RowNumber eq come DerivedTable e la clausola where come dove DerivedTable.RowNumber> 0. A mio parere, questo sarà molto meno confuso tra 6 mesi quando il codice non è fresco nella tua mente.
Edward Comeau

2
@EdwardComeau: rnè un acronimo universalmente accettato per numero di riga in questi giorni. Prova a digitare "row_number over as ..." nella stringa di ricerca di Google e guarda cosa ti suggerisce.
Quassnoi

3
@Quassnoi, la leggibilità è la chiave per una buona codifica e lo sforzo cognitivo di tradurre rn (o altri alias abbreviati) si somma per te e per le persone che mantengono il tuo codice. NB, primo colpo Microsoft, SELEZIONA ROW_NUMBER () OVER (ORDER BY SalesYTD DESC) AS Row, ... Inoltre, non mi sono mai imbattuto in rn prima, quindi il tuo chilometraggio in "universale" può variare.
Edward Comeau

1
@Quassnoi, e secondo colpo, articolo SO - stackoverflow.com/questions/961007/how-do-i-use-row-number diverse varianti e non 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

Penso che tu voglia qualcosa del genere:

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

4
Crea un alias per la tabella se la query precedente non funziona per te. Modificare la penultima riga in quanto From V_EMPLOYEE) Aè aggiungere A come alias.
Hammad Khan

7

In risposta ai commenti sulla risposta di rexem, rispetto al fatto che una visualizzazione in linea o CTE sarebbe più veloce, ho riformulato le query per utilizzare una tabella che io e tutti avevamo a disposizione: 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

I piani di query prodotti erano esattamente gli stessi. Mi aspetto che in tutti i casi l'ottimizzatore di query fornisca lo stesso piano, almeno nella semplice sostituzione di CTE con visualizzazione in linea o viceversa.

Naturalmente, prova le tue query sul tuo sistema per vedere se c'è una differenza.

Inoltre, row_number()nella clausola where è presente un errore comune nelle risposte fornite su Stack Overflow. Logicamente row_number()non è disponibile finché non viene elaborata la clausola select. Le persone lo dimenticano e quando rispondono senza testare la risposta, la risposta a volte è sbagliata. (Un'accusa di cui sono stato colpevole.)


1
Grazie Shannon. Quale versione di SQL Server stavi utilizzando?
OMG Ponies

1
Quindi significa che la risposta fornita in quel link è sbagliata? Ma la persona che ha pubblicato la domanda ha convenuto che sta funzionando .. Sorprendente .. :-)

2
@ Joseph, ma se guardi un'altra risposta pubblicata dall'OP nella domanda collegata, vedrai che si collega a una versione del codice che non è la stessa della risposta accettata. Non so perché abbia accettato la risposta, anche se non sarebbe stata eseguita come inserita. Forse è stato modificato ad un certo punto dopo essere stato accettato, forse è stato sufficiente per farlo andare avanti, anche senza essere totalmente corretto.
Shannon Severance

1
@Rexem: sia SQL Server 2005 che SQL Server 2008. Le versioni precedenti non supportano CTE o ROW_NUMBER ()
Shannon Severance

6

Mi sento come se tutte le risposte che mostrano l'uso di una CTE o di una sottoquery siano soluzioni sufficienti per questo, ma non vedo nessuno arrivare al cuore del motivo per cui OP ha un problema. Il motivo per cui ciò che OP suggerito non funziona è dovuto all'ordine di elaborazione delle query logiche qui:

  1. A PARTIRE DAL
  2. SOPRA
  3. ADERIRE
  4. DOVE
  5. RAGGRUPPA PER
  6. CON CUBO / ROLLUP
  7. AVERE
  8. SELEZIONARE
  9. DISTINTO
  10. ORDINATO DA
  11. SUPERIORE
  12. OFFSET / FETCH

Credo che questo contribuisca notevolmente alla risposta, perché spiega perché si verificano problemi come questo. WHEREviene sempre elaborato prima di SELECTrendere necessaria una CTE o una sottoquery per molte funzioni. Lo vedrai molto in SQL Server.


4

Utilizzando 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

Utilizzo della visualizzazione in linea / alternativa equivalente senza CTE:

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
Quale migliore in termini di prestazioni? Usi CTE o subquery? grazie

1
Vedi la risposta di Shannon: nel suo test sono uguali.
OMG Ponies

6
No, non è più veloce. Le visualizzazioni in SQL Server, CTEe inline sono la stessa cosa e hanno le stesse prestazioni. Quando le funzioni non deterministiche vengono utilizzate in a CTE, viene rivalutato ad ogni chiamata. Bisogna usare trucchi sporchi per forzare la materializzazione di un file CTE. Vedi questi articoli nel mio blog: explainextended.com/2009/07/28/... explainextended.com/2009/05/28/generating-xml-in-subqueries
Quassnoi

2

in base alla risposta di OP alla domanda:

Si prega di vedere questo collegamento. Ha una soluzione diversa, che sembra funzionare per la persona che ha posto la domanda. Sto cercando di trovare una soluzione come questa.

Query impaginata utilizzando l'ordinamento su colonne diverse utilizzando ROW_NUMBER () OVER () in SQL Server 2005

~ Joseph

"metodo 1" è come la query dell'OP dalla domanda collegata, e "metodo 2" è come la domanda dalla risposta selezionata. Dovevi guardare il codice collegato in questa risposta per vedere cosa stava realmente succedendo, poiché il codice nella risposta selezionata è stato modificato per farlo funzionare. Prova questo:

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

PRODUZIONE:

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
fyi, quando si utilizza SET SHOWPLAN_ALL ON il metodo 1 aveva un TotalSubtreeCost di 0,08424953, mentre il metodo 2 era a 0,02627153. il metodo 2 era tre volte migliore.
KM.

1
@rexem, entrambi i metodi 1 e 2 utilizzano CTE, il modo in cui impaginano e ordinano le righe è diverso. Non sono sicuro del motivo per cui questa domanda reale sia così diversa dalla domanda a cui si collega l'OP (nella risposta a questa domanda da parte dell'OP), ma la mia risposta crea un codice funzionante basato sul collegamento a cui si riferisce l'OP
KM.

1
Grazie, sto cercando di confrontare il vecchio post e questo risponde. [Non so come formattarlo] Ecco la risposta fornita da Tomalak. stackoverflow.com/questions/230058?sort=votes#sort-top È sbagliato? Se ha pubblicato solo la metà della risposta, come andrò avanti con il suo modo migliore per eseguire la mia query? Per favore dammi ancora un po 'di luce per procedere .. grazie

@Joseph, la risposta selezionata nel link che fornisci ( stackoverflow.com/questions/230058?sort=votes#sort-top ) è diversa dal codice funzionante che la persona che pone la domanda fornisce mentre lavora nella sua risposta: stackoverflow.com/ domande / 230058 / ... se leggi quella risposta vedrai un link al loro codice: pastebin.com/f26a4b403 e un link alla loro versione di Tomalak: pastebin.com/f4db89a8e nella mia risposta fornisco una versione funzionante di ogni versione usando variabili di tabella
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
Benvenuto in Stack Overflow! Sebbene questo frammento di codice possa essere la soluzione, includere una spiegazione aiuta davvero a migliorare la qualità del tuo post. Ricorda che stai rispondendo alla domanda per i lettori in futuro e quelle persone potrebbero non conoscere i motivi del tuo suggerimento sul codice.
Johan

Aggiungi un po 'di contesto allo snippet di codice a vantaggio dei futuri lettori.
DebanjanB
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.