2009-09-23 19 views
63

Ho trovato una risposta alla domanda con la funzione Row_Number() nella clausola where. Quando ho provato una query, mi è stato sempre il seguente errore:Funzione SQL Row_Number() in Where Clausola

"Msg 4108, Level 15, State 1, Line 1 Windowed functions can only appear in the SELECT or ORDER BY clauses."

Ecco l'interrogazione ho provato. Se qualcuno sa come risolvere questo, per favore fatemelo sapere.

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

'ROW_NUMBER() OVER (ORDER BY id_dipendente)> 0' valuterà sempre a 'TRUE' – Quassnoi

+1

Sì , giusto. Non sono preoccupato per la condizione, che posso cambiare in qualsiasi momento. Voglio che la query funzioni prima, quindi penso di mantenere il numero di riferimento tra 500 e 800 ... grazie –

+1

@Joseph: Perché stai cercando di evitare l'uso di un CTE? –

risposta

49
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 

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

+0

Eccellente. Questo mi ha aiutato un sacco. Grazie. –

+0

@Quassnoi Grazie! Curiosità: perché c'è 'q'? Cosa significa? –

+1

@ DavideChicco.it: in SQL Server, le tabelle derivate richiedono un alias (avrei dovuto invece scrivere 'AS q', ma funzionerebbe anche in questo caso). – Quassnoi

69

Per aggirare questo problema, racchiudere l'istruzione select in un CTE e quindi eseguire una query sul CTE e utilizzare i risultati della funzione windowed 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 
+4

Sto cercando di evitare CTE. Questo è il caso peggiore che sto cercando. grazie –

+2

Potrebbe essere più veloce se si utilizza una sottoquery anziché una CTE. Ho visto prestazioni migliori di un fattore 1,5 in alcuni casi –

+0

Posso utilizzare Con nella query secondaria – Xulfee

14

Penso che si desidera qualcosa di simile:

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

Crea un alias per la tabella se la query precedente non funziona per te. Modifica seconda ultima riga come 'Da V_EMPLOYEE) A' che è aggiungere A come alias. –

3

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 

Utilizzando vista in linea/non-CTE alternativo equivalente:

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 
+0

Quale migliore in termini di prestazioni? Usando CTE o sottoquery? grazie –

+0

Vedere la risposta di Shannon - nel suo test sono uguali. –

+5

No, non è più veloce. In 'SQL Server',' CTE' e le viste in linea sono la stessa cosa e hanno le stesse prestazioni. Quando le funzioni non deterministiche sono utilizzate in un 'CTE', è rivalutato su ogni chiamata. Uno deve usare trucchi sporchi per forzare la materializzazione di un 'CTE'. Vedi questi articoli nel mio blog: http: // explaninextended.it/2009/07/28/sql-server-random-records-avoiding-cte-reevaluation/http://explainextended.com/2009/05/28/generating-xml-in-subqueries/ – Quassnoi

5

In risposta ai commenti su rexem's risposta, per quanto riguarda se una vista in linea o CTE sarebbe più veloce, ho rifuso le query per usare una tabella I, e tutti, avevano 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, Query Optimizer crei lo stesso piano, almeno nella semplice sostituzione di CTE con vista in linea o viceversa.

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

Inoltre, row_number() nella clausola where è un errore comune nelle risposte fornite su Stack Overflow. Logicaly row_number() non è disponibile finché non viene elaborata la clausola select. Le persone dimenticano questo e quando rispondono senza testare la risposta, la risposta a volte è sbagliata. (Una carica Io stesso ho reso colpevole di.)

+0

Thx Shannon. Quale versione di SQL Server stai usando? –

+0

In questo modo, la risposta fornita in quel collegamento è errata? Ma la persona che ha postato la domanda ha concordato che funziona .. Sorprendente .. :-) –

+1

@Joseph, ma se guardi un'altra risposta postata dall'OP nella domanda collegata, vedrai che si collega a una versione di il codice che non è lo stesso della risposta accettata. Non so perché abbia accettato la risposta, anche se non sarebbe stata pubblicata. Forse è stato modificato ad un certo punto dopo essere stato accettato, forse è stato sufficiente per farlo andare, anche senza essere completamente corretto. –

1

in base alla risposta del PO alla domanda:

Please see this link. Its having a different solution, which looks working for the person who asked the question. I'm trying to figure out a solution like this.

Paginated query using sorting on different columns using ROW_NUMBER() OVER() in SQL Server 2005

~Joseph

"Metodo 1" è come interrogazione del PO dalla questione legata, e "il metodo 2" è come la query dalla risposta selezionata. Dovevi controllare il codice collegato in questo answer per vedere cosa stava realmente accadendo, 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 

USCITA:

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) 
+0

fyi, quando si utilizza _SET SHOWPLAN_ALL ON_ il metodo 1 aveva un TotalSubtreeCost di 0.08424953, mentre il metodo 2 era 0.02627153. il metodo 2 era oltre tre volte migliore. –

+0

Scusa - il metodo 2 è la vista in linea o CTE, corretta? –

+0

@rexem, entrambi i metodi 1 e 2 usano CTE, il modo in cui paginano e ordinano le righe è diverso. Non sono sicuro del motivo per cui questa domanda reale sia così diversa dalla domanda a cui l'OP si collega (nella risposta a questa domanda dall'OP), ma la mia risposta crea codice funzionante basato sul link che l'OP fa riferimento a –

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

buon esempio! grazie mille – richin

+1

Grazie per l'ottimo esempio! Questo mi è stato di grande aiuto, poiché avevo bisogno di questa funzionalità per lavorare su un vecchio database SQL Server 2000 in cui non esistono CTE. – StarPilot

-1
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

se è necessario aggiungere condizioni dinamicamente, è possibile utilizzare esiste in tua ricerca:

SELECT f0.* 
FROM FOO f0 
WHERE EXISTS 
    (SELECT f2.foo_id 
    FROM 
    (SELECT foo_id , 
     ROW_NUMBER() OVER(PARTITION BY F1.BAR_ID ORDER BY F1.AMOUNT) rk 
    FROM foo f1 
    )f2 
    WHERE f0.foo_id=f2.foo_id 
    AND rk   =2 -- your condition on row_number goes here 
);