多个左连接 – 怎么样?

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

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

我有以下SQL,它应该根据他们在Choices和Bets上的总奖金给我一系列用户..但它给了我一些错误的总胜利,我认为问题出在Left Joins中,因为如果我重写SQL到只包含选择或赌注表然后我工作得很好..

任何有关如何重写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 | +---------------+ 

这是因为两个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 

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

你的问题是你正在吹灭你的数据集。 如果你做了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 

在与发条类似的解决方案中,由于每个表的列是相同的,所以我会将它们预先合并,然后将它们相加。 因此,在大多数情况下,内部查询将为每个用户提供两条记录…一条用于投注,一条用于选择 – 每条记录在执行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