多个左连接 - 如何?

时间:2012-11-30 19:02:42

标签: sql ruby-on-rails join sum left-join

我在Heroku运行了一个Rails应用程序,我正在尝试计算用户在高分列表中的排名(位置)。

应用程序是用户互相下注的地方,可以开始下注(创建选项)或者他们可以对已经创建的选项下注(通过下注)。

我有以下SQL,它应该根据他们在选择和投注上的总奖金给我一系列用户..但是它给了我一些错误的总胜利,我认为问题在于左连接,因为如果我重写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 |
+---------------+

下面是两个SQL字符串,其中我只加入一个表,两个结果来自那个..看到下面的总和与上面的结果不匹配..下面是正确的总和。

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

3 个答案:

答案 0 :(得分:1)

这是因为两个LEFT JOIN ed表之间的关系 - 也就是说,如果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

(另外,我认为惯例是命名表单数,而不是复数。)

答案 1 :(得分: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

答案 2 :(得分:0)

在与发条相似的解决方案中,由于每个表的列是相同的,我会将它们预先合并,然后将它们相加。因此,在大多数情况下,内部查询将为每个用户提供两个记录...一个用于下注,一个用于选择 - 每个分别在执行UNION ALL时预先求和。然后,简单的join / sum来获得结果

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