1

Heroku で実行している Rails アプリがあり、ハイスコア リストに対するユーザーのランク (位置) を計算しようとしています。

このアプリは、ユーザーが互いに賭けを行う場所であり、賭けを開始する (CHOICE を作成する) か、既に作成された Choice に対して (BET を行うことによって) 賭けることができます。

次の SQL を使用すると、Choices と Bets の両方での合計賞金に基づいてユーザーの配列が得られます。しかし、間違った合計賞金が得られます。SQL をChoice テーブルまたは Bet テーブルのみが含まれている場合、問題なく動作します。

正しく動作するように SQL を書き直す方法についての指針を持っている人:)

SELECT users.id, sum(COALESCE(bets.profitloss, 0) + COALESCE(choices.profitloss, 0)) as total_pl
FROM users
LEFT JOIN bets ON bets.user_id = users.id
LEFT JOIN choices ON choices.user_id = users.id
GROUP BY users.id
ORDER BY total_pl DESC

結果:

+---------------+
| id | total_pl |
+---------------+
|  1 |      830 |
|  4 |      200 |
|  3 |      130 |
|  7 |     -220 |
|  5 |    -1360 |
|  6 |    -4950 |
+---------------+

以下は、1 つのテーブルにのみ結合する 2 つの SQL 文字列と、そこからの 2 つの結果です。以下の合計が上記の結果と一致しないことを確認してください。以下は正しい合計です。

SELECT users.id, sum(COALESCE(bets.profitloss, 0)) as total_pl 
FROM users 
LEFT JOIN bets ON bets.user_id = users.id 
GROUP BY users.id 
ORDER BY total_pl DESC

SELECT users.id, sum(COALESCE(choices.profitloss, 0)) as total_pl 
FROM users 
LEFT JOIN choices ON choices.user_id = users.id 
GROUP BY users.id 
ORDER BY total_pl DESC

+---------------+
| id | total_pl | 
+---------------+
|  3 |      170 |
|  1 |      150 |
|  4 |      100 |
|  5 |       80 |
|  7 |       20 |
|  6 |      -30 |
+---------------+

+---------------+
| id | total_pl |
+---------------+
|  1 |       20 |
|  4 |        0 |
|  3 |      -10 |
|  7 |      -30 |
|  5 |     -110 |
|  6 |     -360 |
+---------------+
4

3 に答える 3

1

あなたの問題は、データセットを吹き飛ばしていることです。SELECT * を実行すると、それを見ることができます。これを試して。テーブルがないためテストできませんでしたが、動作するはずです

SELECT
 totals.id
 ,SUM(totals.total_pl) total_pl
FROM
(
  SELECT users.id, sum(COALESCE(bets.profitloss, 0)) as total_pl 
  FROM users 
  LEFT JOIN bets ON bets.user_id = users.id 
  GROUP BY users.id 

  UNION ALL SELECT users.id, sum(COALESCE(choices.profitloss, 0)) as total_pl 
  FROM users 
  LEFT JOIN choices ON choices.user_id = users.id 
  GROUP BY users.id 
 ) totals
GROUP BY  totals.id
ORDER BY total_pl DESC
于 2012-11-30T19:52:19.757 に答える
1

これは、2 つの ed テーブル間の関係が原因で発生しています。つまり、とのLEFT JOIN両方に (複数の) 行がある場合、表示される行の総数は、加算ではなく、個々の行カウントから乗算されます。 あなたが持っている場合betschoices

choices
id   profitloss
================
1    20
1    30

bets
id   profitloss
================
1    25
1    35

結合の結果は実際には次のようになります。

bets/choices
id   bets.profitloss   choices.profitloss
1    20                25
1    20                35
1    30                25
1    30                35

(これがどこに向かっているのかわかりますか?)

これを修正するのは、実際にはかなり簡単です。RDBMS を指定していませんが、これはそれらのいずれでも (または微調整で) 動作するはずです。

SELECT users.id, COALESCE(bets.profitloss, 0) 
                     + COALESCE(choices.profitloss, 0) as total_pl
FROM users
LEFT JOIN (SELECT user_id, SUM(profitloss) as profitloss
           FROM bets
           GROUP BY user_id) bets
ON bets.user_id = users.id
LEFT JOIN (SELECT user_id, SUM(profitloss) as profitloss
           FROM choices
           GROUP BY user_id) choices
ON choices.user_id = users.id
ORDER BY total_pl DESC

(また、テーブルの名前は複数形ではなく単数形にするのが慣習だと思います。)

于 2012-11-30T19:46:37.613 に答える
0

Clockwork と同様のソリューションでは、列はテーブルごとに同じであるため、それらを事前結合して合計するだけです。したがって、ほとんどの場合、内部クエリにはユーザーごとに 2 つのレコードがあります... 1 つは賭け用、もう 1 つは選択肢用です。それぞれ UNION ALL を実行してから事前に合計されます。次に、単純な結合/合計で結果を取得します

select 
      U.userid,
      sum( coalesce( PreSum.profit, 0) ) as TotalPL
   from
      Users U
         LEFT JOIN
            ( select user_id, sum( profitloss ) as Profit
                 from bets
                 group by user_id
              UNION ALL
              select user_id, sum( profitloss ) as Profit
                 from choices
                 group by user_id ) PreSum
            on U.ID = PreSum.User_ID
   group by
      U.ID
于 2012-11-30T19:57:27.297 に答える