2013-03-13 33 views
8

Tôi có một bảng trong SQL Server 2012 như ảnh chụp cho thấy:SQL: Last_Value() trả về kết quả sai (nhưng FIRST_VALUE() hoạt động tốt)

enter image description here

Sau đó, tôi đang sử dụng Last_Value() và Giá trị đầu tiên để có được AverageAmount của mỗi EmpID cho YearMonth khác nhau. Các kịch bản như sau:

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 

Tuy nhiên, kết quả cho truy vấn này là:

result

Trong cột "201112AvgAmount", nó cho thấy các giá trị khác nhau cho mỗi EmpID khi "200901AvgAmount" có giá trị chính xác.

Có điều gì sai với tập lệnh SQL của tôi không? Tôi đã thực hiện rất nhiều nghiên cứu trực tuyến nhưng vẫn không thể tìm thấy câu trả lời ....

Trả lời

9

Không có gì sai với tập lệnh của bạn, đây là cách phân vùng hoạt động trong máy chủ SQL: /. Nếu bạn thay đổi kết quả LAST_VALUE thành MAX sẽ giống nhau. Giải pháp sẽ là:

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 

Có bài đăng tuyệt vời về nó, link. GL!

+0

Cảm ơn bạn rất nhiều! Không chỉ giải quyết vấn đề mà còn cho tôi biết lý do tại sao. Tôi đã duyệt qua bài đăng được liên kết trước đó nhưng không nghĩ về cơ chế phân vùng. – Echo

14

Dưới đây là một truy vấn nhanh để minh họa cho hành vi:

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) 

Kết quả của các truy vấn trên có thể được nhìn thấy ở đây (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 | 

Rất ít người nghĩ đến những khung ngầm rằng là áp dụng cho các chức năng cửa sổ có mệnh đề là ORDER BY. Trong trường hợp này, các cửa sổ được mặc định là khung RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW. (RANGE không chính xác giống như ROWS, nhưng đó là một câu chuyện khác).Hãy suy nghĩ về nó theo cách này:

  • Trên hàng với v = 1 khung cửa sổ lệnh của nhịp v IN (1)
  • Trên hàng với v = 2 khung cửa sổ lệnh của nhịp v IN (1, 2)
  • Trên hàng với v = 3 nhịp khung cửa sổ lệnh của v IN (1, 2, 3)
  • Trên hàng có v = 4 khoảng cách khung cửa sổ đã đặt hàng v IN (1, 2, 3, 4)

Nếu bạn muốn ngăn chặn hành vi đó, bạn có hai lựa chọn:

  • Sử dụng một rõ ràng ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING khoản cho ra lệnh chức năng cửa sổ
  • Sử dụng không ORDER BY điều khoản trong những chức năng cửa sổ cho phép bỏ qua chúng (như MAX(v) OVER())

Thông tin chi tiết được giải thích trong this article about LEAD(), LAG(), FIRST_VALUE() and LAST_VALUE()

0

Cách đơn giản nhất là lặp lại truy vấn của bạn bằng cách sử dụng first_value, chỉ cần đặt lệnh asc cho trường hợp thứ nhất và desc cho trường hợp thứ 2.

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 
Các vấn đề liên quan