2012-01-13 18 views
10

Tôi đang cố gắng kết hợp một truy vấn sẽ truy lục số liệu thống kê của người dùng (lợi nhuận/thua lỗ) dưới dạng kết quả tích lũy, trong một khoảng thời gian.Chức năng và nhóm cửa sổ Postgres theo ngoại lệ

Dưới đây là các truy vấn tôi có cho đến nay:

SELECT p.name, e.date, 
    sum(sp.payout) OVER (ORDER BY e.date) 
    - sum(s.buyin) OVER (ORDER BY e.date) AS "Profit/Loss" 
FROM result r 
    JOIN game g ON r.game_id = g.game_id 
    JOIN event e ON g.event_id = e.event_id 
    JOIN structure s ON g.structure_id = s.structure_id 
    JOIN structure_payout sp ON g.structure_id = sp.structure_id 
          AND r.position = sp.position 
    JOIN player p ON r.player_id = p.player_id 
WHERE p.player_id = 17 
GROUP BY p.name, e.date, e.event_id, sp.payout, s.buyin 
ORDER BY p.name, e.date ASC 

Truy vấn sẽ chạy. Tuy nhiên, kết quả hơi sai. Lý do là một event có thể có nhiều trò chơi (với khác nhau sp.payouts). Do đó, ở trên có nhiều hàng nếu người dùng có 2 kết quả trong một sự kiện với các khoản thanh toán khác nhau (nghĩa là có 4 trò chơi cho mỗi sự kiện và người dùng nhận được 20 đô la từ một sự kiện và 40 đô la từ một người khác).

Các giải pháp hiển nhiên sẽ là sửa đổi GROUP BY tới:

GROUP BY p.name, e.date, e.event_id 

Tuy nhiên, Postgres phàn nàn ở đây là nó không xuất hiện để được thừa nhận rằng sp.payouts.buyin là bên trong một chức năng tổng hợp. Tôi gặp lỗi:

column "sp.payout" must appear in the GROUP BY clause or be used in an aggregate function

Tôi đang chạy 9.1 trên máy chủ Ubuntu Linux.
Tôi có thiếu thứ gì đó hay đây có phải là lỗi chính hãng trong Postgres không?

Trả lời

21

Bạn đang không phải, trên thực tế, sử dụng các hàm tổng hợp. Bạn đang sử dụng window functions. Đó là lý do tại sao PostgreSQL yêu cầu sp.payouts.buyin để được bao gồm trong mệnh đề GROUP BY.

Bằng cách phụ thêm một khoản OVER, hàm tổng hợp sum() được biến thành một chức năng cửa sổ, trong đó tập hợp các giá trị cho mỗi phân vùng trong khi giữ tất cả các hàng.

Bạn có thể kết hợp chức năng cửa sổ và chức năng tổng hợp. Tổng hợp được áp dụng trước tiên. Tôi không hiểu từ mô tả của bạn về cách bạn muốn xử lý nhiều khoản thanh toán/lần mua mỗi sự kiện. Theo dự đoán, tôi tính toán tổng số tiền cho mỗi sự kiện. Bây giờ tôi có thể loại bỏ sp.payouts.buyin từ mệnh đề GROUP BY và nhận được một hàng cho mỗi playerevent:

SELECT p.name 
    , e.event_id 
    , e.date 
    , sum(sum(sp.payout)) OVER w 
    - sum(sum(s.buyin )) OVER w AS "Profit/Loss" 
FROM player   p 
JOIN result   r ON r.player_id  = p.player_id 
JOIN game    g ON g.game_id  = r.game_id 
JOIN event    e ON e.event_id  = g.event_id 
JOIN structure   s ON s.structure_id = g.structure_id 
JOIN structure_payout sp ON sp.structure_id = g.structure_id 
          AND sp.position  = r.position 
WHERE p.player_id = 17 
GROUP BY e.event_id 
WINDOW w AS (ORDER BY e.date, e.event_id) 
ORDER BY e.date, e.event_id; 

Trong cụm từ này: sum(sum(sp.payout)) OVER w, bên ngoài sum() là một chức năng cửa sổ, khu vực nội sum() là một chức năng tổng hợp .

Giả sử p.player_ide.event_idPRIMARY KEY trong bảng tương ứng.

Tôi đã thêm e.event_id vào số ORDER BY của mệnh đề WINDOW để đến một thứ tự sắp xếp xác định. (Có thể có nhiều sự kiện trong cùng một ngày.) Cũng bao gồm event_id trong kết quả để phân biệt nhiều sự kiện mỗi ngày.

Trong khi truy vấn hạn chế đến một chơi đơn (WHERE p.player_id = 17), chúng tôi không cần phải thêm p.name hoặc p.player_id-GROUP BYORDER BY. Nếu một trong các phép nối sẽ nhân các hàng không đúng, tổng kết quả sẽ không chính xác (một phần hoặc hoàn toàn nhân). Nhóm theo p.name không thể sửa chữa truy vấn sau đó.

Tôi cũng đã xóa e.date khỏi mệnh đề GROUP BY. Khóa chính e.event_id bao gồm tất cả các cột của hàng nhập since PostgreSQL 9.1.

Nếu bạn thay đổi các truy vấn để trả lại nhiều người chơi cùng một lúc, điều chỉnh: (?)

... 
WHERE p.player_id < 17 -- example - multiple players 
GROUP BY p.name, p.player_id, e.date, e.event_id -- e.date and p.name redundant 
WINDOW w AS (ORDER BY p.name, p.player_id, e.date, e.event_id) 
ORDER BY p.name, p.player_id, e.date, e.event_id; 

Trừ p.name được định nghĩa độc đáo, nhóm và trật tự bởi player_id bổ sung để có được kết quả chính xác trong một thứ tự sắp xếp xác định.

Tôi chỉ giữ e.datep.name trong GROUP BY để có thứ tự sắp xếp giống nhau trong tất cả các mệnh đề, hy vọng có lợi ích hiệu suất. Khác, bạn có thể loại bỏ các cột ở đó. (Tương tự với chỉ e.date trong truy vấn đầu tiên.)

+0

Truy vấn đầu tiên đang hoạt động, tuy nhiên, đầu ra của truy vấn không đưa ra kết quả được yêu cầu. Tôi có thể thấy những gì sửa đổi sẽ làm việc trong lý thuyết, nhưng Postgres không thích nó. Tôi sẽ thử những điều sau và cho bạn biết. tuy nhiên, có vẻ như sẽ có 2 hàng trong kết quả truy vấn của bạn nếu một "event_id" có nhiều hơn một "khoản thanh toán". – Martin

+0

Tôi vừa thử nó với các sửa đổi bạn đã đề xuất, và nó trở lại với nhiều hàng, nơi có nhiều giá trị sp.payout cho một event_id duy nhất. – Martin

+0

@Martin: Xem câu trả lời đã sửa đổi của tôi. –