2013-03-13 8 views
8

Ho una tabella in SQL Server 2012 come l'istantanea mostra:SQL: LAST_VALUE() restituisce risultato sbagliato (ma FIRST_VALUE() funziona bene)

enter image description here

Poi sto usando LAST_VALUE() e Primo valore per ottenere AverageAmount di ogni EmpID per YearMonth diversi. Lo script è il seguente:

SELECT A.EmpID, 
     First_Value(A.AverageAmount) OVER (PARTITION BY A.EmpID Order by A.DimYearMonthKey asc) AS '200901AvgAmount', 
     Last_Value(A.AverageAmount) OVER (PARTITION BY A.EmpID Order by A.DimYearMonthKey asc) AS '201112AvgAmount' 

FROM Emp_Amt AS A 

Tuttavia, il risultato per questa ricerca è:

result

Nella colonna "201112AvgAmount", mostra valori diversi per ciascun EmpID mentre "200901AvgAmount" contiene valori corretti

C'è qualcosa di sbagliato nel mio script SQL? Ho fatto molte ricerche online ma non riesco a trovare la risposta ....

risposta

9

Non c'è niente di sbagliato nel tuo script, questo è un modo in cui il partizionamento funziona in SQL server: /. Se cambi LAST_VALUE in MAX il risultato sarà lo stesso. Soluzione potrebbe essere:

SELECT A.EmpID, 
     First_Value(A.AverageAmount) OVER (PARTITION BY A.EmpID Order by A.DimYearMonthKey asc) AS '200901AvgAmount', 
     Last_Value(A.AverageAmount) OVER (PARTITION BY A.EmpID Order by A.DimYearMonthKey ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS '201112AvgAmount' 
FROM Emp_Amt AS A 

V'è un grande post su di esso, link. GL!

+0

Grazie mille! Non solo ha risolto il problema ma mi dice anche perché. Ho sfogliato il post collegato in precedenza ma non ho pensato al meccanismo di partizionamento. – Echo

14

ecco una rapida query per illustrare il comportamento:

select 
    v, 

    -- FIRST_VALUE() and LAST_VALUE() 
    first_value(v) over(order by v) f1, 
    first_value(v) over(order by v rows between unbounded preceding and current row) f2, 
    first_value(v) over(order by v rows between unbounded preceding and unbounded following) f3, 
    last_value (v) over(order by v) l1, 
    last_value (v) over(order by v rows between unbounded preceding and current row) l2, 
    last_value (v) over(order by v rows between unbounded preceding and unbounded following) l3, 

    -- For completeness' sake, let's also compare the above with MAX() 
    max  (v) over() m1, 
    max  (v) over(order by v) m2, 
    max  (v) over(order by v rows between unbounded preceding and current row) m3, 
    max  (v) over(order by v rows between unbounded preceding and unbounded following) m4 
from (values(1),(2),(3),(4)) t(v) 

L'output della query di cui sopra può essere visto qui (SQLFiddle here):

| V | F1 | F2 | F3 | L1 | L2 | L3 | M1 | M2 | M3 | M4 | 
|---|----|----|----|----|----|----|----|----|----|----| 
| 1 | 1 | 1 | 1 | 1 | 1 | 4 | 4 | 1 | 1 | 4 | 
| 2 | 1 | 1 | 1 | 2 | 2 | 4 | 4 | 2 | 2 | 4 | 
| 3 | 1 | 1 | 1 | 3 | 3 | 4 | 4 | 3 | 3 | 4 | 
| 4 | 1 | 1 | 1 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 

poche persone pensano delle cornici implicite che sono applicato alle funzioni della finestra che accettano una clausola ORDER BY. In questo caso, le finestre sono predefinite per il frame RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW. (La gamma non è esattamente la stessa di ROWS, ma questa è un'altra storia).Pensare in questo modo:

  • Sulla riga con v = 1 telaio della finestra ordinato estende v IN (1)
  • Sulla riga con v = 2 telaio della finestra ordinato estende v IN (1, 2)
  • Sulla riga con v = 3 campate telaio della finestra ordinata v IN (1, 2, 3)
  • Sulla riga con v = 4 telaio della finestra ordinato estende v IN (1, 2, 3, 4)

Se si vuole evitare che il comportamento, si hanno due opzioni:

  • utilizzare un ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING clausola esplicita per ordinato funzioni finestra
  • uso non ORDER BY clausola in quelle funzioni finestra che consentono loro omissione (come MAX(v) OVER())

Maggiori dettagli sono spiegati in this article about LEAD(), LAG(), FIRST_VALUE() and LAST_VALUE()

0

Il modo più semplice è ripetere la query utilizzando first_value, basta effettuare l'ordine come asc per il primo caso e desc per il secondo caso.

SELECT A.EmpID, 
     First_Value(A.AverageAmount) OVER (PARTITION BY A.EmpID Order by A.DimYearMonthKey asc) AS '200901AvgAmount', 
     First_Value(A.AverageAmount) OVER (PARTITION BY A.EmpID Order by A.DimYearMonthKey desc) AS '201112AvgAmount' 

FROM Emp_Amt AS A