SQL: ostatnia wartość () zwraca zły wynik (ale pierwsza wartość () działa dobrze)

Mam tabelę w SQL Server 2012 Jak pokazuje migawka:

Tutaj wpisz opis obrazka

Następnie używam last_value () i First Value, aby uzyskać średnią ilość każdego EmpID dla różnych lat. Skrypt wygląda następująco:

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

Jednak wynik dla tego zapytania to:

wynik

W kolumnie "201112avgamount" pokazuje różne wartości dla każdego EmpID, podczas gdy "200901avgamount" ma prawidłowe wartości.

Czy jest coś nie tak z moim skryptem SQL? Zrobiłem wiele badań online, ale nadal nie mogę znaleźć odpowiedzi....

Author: Echo, 2013-03-13

3 answers

Nie ma nic złego w Twoim skrypcie, tak działa partycjonowanie w SQL server :/. Jeśli zmienisz wartość LAST_VALUE na MAX wynik będzie taki sam. Rozwiązaniem byłoby:

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

Jest świetny post o tym, link . GL!

 13
Author: www,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-13 16:11:49

Oto szybkie zapytanie, aby zilustrować zachowanie:

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)

Wyjście powyższego zapytania można zobaczyć tutaj ( sqlfiddle tutaj):

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

Niewiele osób myśli o niejawnych ramkach, które są stosowane do funkcji okien, które przyjmują klauzulę ORDER BY. W tym przypadku okna są domyślne do ramki RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW. (Zakres nie jest dokładnie taki sam jak wiersze, ale to już inna historia). Pomyśl o tym w ten sposób:

  • w rzędzie z v = 1 uporządkowaną ramką okna spans v IN (1)
  • w wierszu z v = 2 ramka okna uporządkowanego rozciąga się v IN (1, 2)
  • w wierszu z v = 3 rama okna uporządkowanego rozciąga się v IN (1, 2, 3)
  • w wierszu z v = 4 rama okna uporządkowanego rozciąga się v IN (1, 2, 3, 4)

Jeśli chcesz zapobiec takiemu zachowaniu, masz dwie opcje:

  • użyj jawnej klauzuli ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING dla uporządkowanych funkcji okien
  • użyj klauzuli ORDER BY w tych funkcjach okien, które pozwalają na ich pominięcie (jako MAX(v) OVER())

Więcej szczegółów znajdziesz w tym artykule o LEAD(), LAG(), FIRST_VALUE() oraz LAST_VALUE()

 19
Author: Lukas Eder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-23 16:37:48

Najprostszym sposobem jest powtórzenie zapytania za pomocą first_value, po prostu zrób porządek jako asc dla 1. przypadku i desc dla 2. przypadku.

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
 0
Author: Vishnu Kumar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-24 13:14:12