2013-03-13 5 views
8

ich eine Tabelle in SQL Server 2012 haben, wie der Schnappschuss zeigt:SQL: LAST_VALUE() gibt falsche Ergebnis (aber FIRST_VALUE() funktioniert gut)

enter image description here

Dann bin ich mit LAST_VALUE() und Erster Wert, um AverageAmount für jede EmpID für verschiedene YearMonth zu erhalten. Das Skript wird wie folgt dar:

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 

das Ergebnis dieser Abfrage ist jedoch:

result

In Spalte "201112AvgAmount", zeigt es verschiedene Werte für jede EmpID während "200901AvgAmount" hat korrekte Werte.

Gibt es etwas falsch mit meinem SQL-Skript? Ich habe viel online recherchiert aber kann immer noch nicht die Antwort finden ....

Antwort

9

Es ist nichts falsch mit Ihrem Skript, das ist ein Weg, wie Partitionierung in SQL Server funktioniert: /. Wenn Sie LAST_VALUE in MAX ändern, wird das Ergebnis gleich sein. Lösung wäre:

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 

Es gibt einen großen Beitrag ist über sie, link. GL!

+0

Vielen Dank soooooo viel! Nicht nur das Problem gelöst, sondern auch warum. Ich habe den verlinkten Beitrag schon einmal durchgeblättert, aber nicht über den Partitionierungsmechanismus nachgedacht. – Echo

14

Hier ist eine schnelle Abfrage, das Verhalten zu veranschaulichen:

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) 

Der Ausgang der obigen Abfrage hier zu sehen ist (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 | 

Nur wenige Menschen der impliziten Rahmen denken, die angewendet auf Fensterfunktionen, die eine ORDER BY Klausel annehmen. In diesem Fall sind Fenster standardmäßig auf den Frame RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW eingestellt. (RANGE ist nicht genau dasselbe wie ROWS, aber das ist eine andere Geschichte).Denken Sie an es auf diese Weise:

  • Auf der Zeile mit v = 1 der Rahmen des bestellten Fenster v IN (1) mit v = 2
  • Auf der Reihe erstreckt sich über den Rahmen des bestellten Fenster v IN (1, 2) mit v = 3
  • Auf der Reihe erstreckt sich über den Rahmen des bestellten Fenster Spannweiten v IN (1, 2, 3)
  • auf die Zeile mit dem Rahmen des v = 4 geordneten Fensters umspannt v IN (1, 2, 3, 4)

Wenn Sie dieses Verhalten verhindern wollen, haben Sie zwei Möglichkeiten:

  • Verwenden Sie eine explizite ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING Klausel für bestellt Fensterfunktionen
  • keine ORDER BY Klausel in diesen Fensterfunktionen verwenden, die für das Weglassen ihnen ermöglichen (als MAX(v) OVER())

Weitere Details sind in this article about LEAD(), LAG(), FIRST_VALUE() and LAST_VALUE()

erklärt
0

Der einfachste Weg ist, die Abfrage mit first_value zu wiederholen, nur die Reihenfolge asc für den ersten Fall und desc für den zweiten Fall.

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