16

一定期間の累積結果としてユーザーの統計(利益/損失)を取得するクエリをまとめようとしています。

これが私がこれまでに持っているクエリです:

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

クエリが実行されます。ただし、結果は少し不正確です。その理由は、eventが複数のゲーム(異なるsp.payouts)を持つことができるためです。したがって、ユーザーが異なる支払いのイベントで2つの結果を持っている場合(つまり、イベントごとに4つのゲームがあり、ユーザーは1つから£20、もう1つから£40を取得します)、上記は複数の行で表示されます。

明らかな解決策は、次のように修正するGROUP BYことです。

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

sp.payoutただし、Postgresはそれを認識していないようでs.buyinあり、集計関数内にあるため、これに不満を持っています。エラーが発生します:

列「sp.payout」は、GROUP BY句に含めるか、集計関数で使用する必要があります

UbuntuLinuxサーバーで9.1を実行しています。
私は何かが足りないのですか、それともこれはPostgresの真の欠陥でしょうか?

4

1 に答える 1

40

実際、集計関数を使用していません。ウィンドウ関数を使用しています。そのため、PostgreSQLは条項に含めることを要求sp.payoutしています。s.buyinGROUP BY

OVER句を追加することにより、集計関数sum()はウィンドウ関数に変わります。この関数は、すべての行を保持しながら、パーティションごとに値を集計します。

ウィンドウ関数と集計関数を組み合わせることができます。集計が最初に適用されます。あなたの説明から、イベントごとに複数のペイアウト/バイインをどのように処理したいかがわかりませんでした。推測として、私はイベントごとにそれらの合計を計算します。これで、句からandを削除して、andごとに1つの行を取得できますsp.payouts.buyinGROUP BYplayerevent

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;

この式では、:sum(sum(sp.payout)) OVER w、外側sum()はウィンドウ関数、内側sum()は集約関数です。

と仮定p.player_ide.event_idPRIMARY KEY、それぞれのテーブルにあります。

決定論的なソート順に到達するために、句のを追加e.event_idしました。(同じ日に複数のイベントが発生する可能性があります。)1日あたりの複数のイベントを区別するために、結果にも含まれます。ORDER BYWINDOWevent_id

クエリはシングルプレイヤー( )に制限されていますが、またはを追加する必要はありWHERE p.player_id = 17ません。結合の1つが行を過度に乗算すると、結果の合計は正しくなくなります(部分的または完全に乗算されます)。グループ化すると、クエリを修復できませんでした。p.namep.player_idGROUP BYORDER BYp.name

私も条項e.dateから削除しました。GROUP BY主キーは、PostgreSQL9.1以降e.event_idの入力行のすべての列をカバーします。

一度に複数のプレーヤーを返すようにクエリを変更する場合は、次のように調整します。

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

p.name一意(?)で定義されていない限り、グループ化player_idして順序付けし、決定論的な並べ替え順序で正しい結果を取得します。

パフォーマンスの向上を期待して、すべての句で同じソート順を維持するe.dateだけでしp.nameた。GROUP BYそれ以外の場合は、そこで列を削除できます。e.date(最初のクエリと同様です。)

于 2012-01-13T02:10:07.130 に答える