MySQL使用sum和left join将结果加倍

MySQL使用sum和left join将结果加倍,mysql,sum,left-join,Mysql,Sum,Left Join,我有三张桌子:孩子、玩具和游戏,每一张桌子都有唯一的主键:id_孩子、id_玩具和id_游戏。每个孩子可以有多个玩具和游戏,但每个玩具或游戏只有一个孩子拥有 玩具和游戏有一个包含3个状态的购买栏:-1,0,1 表结构如下所示: kids id_kid kid_name etc games id_game id_kid_games --> links with id_kid in kids_table (maybe not the best name, I know) game_nam

我有三张桌子:孩子、玩具和游戏,每一张桌子都有唯一的主键:id_孩子、id_玩具和id_游戏。每个孩子可以有多个玩具和游戏,但每个玩具或游戏只有一个孩子拥有

玩具和游戏有一个包含3个状态的购买栏:-1,0,1 表结构如下所示:

kids

id_kid
kid_name
etc

games

id_game
id_kid_games --> links with id_kid in kids_table (maybe not the best name, I know)
game_name
bought --> can be -1,0,1

toys

id_toy
id_kid_toys --> links with id_kid in kids_table
toy_name
bought --> can be -1,0,1
对于每个孩子,我尝试使用下面的查询获取玩具和游戏的总数,购买的和未购买的,但是结果是双倍的:

SELECT kids.*, 

COUNT(DISTINCT toys.id_toy) AS total_toys, 
SUM(CASE toys.bought WHEN 1 THEN 1 ELSE 0 END) AS toys_bought, 
SUM(CASE toys.bought WHEN -1 THEN 1 ELSE 0 END) AS toys_not_bought, 

COUNT(DISTINCT games.id_game) AS total_games, 
SUM(CASE games.bought WHEN 1 THEN 1 ELSE 0 END) AS games_bought, 
SUM(CASE games.bought WHEN -1 THEN 1 ELSE 0 END) AS games_not_bought 

FROM kids as k 
LEFT JOIN toys t ON k.id_kid = t.id_kid_toys
LEFT JOIN games g ON k.id_kid = g.id_kid_games
GROUP BY k.id_kid
ORDER BY k.name ASC
一个孩子有2个玩具和4个游戏,都买了,结果是2个玩具正确,4个游戏正确,8个玩具正确,8个游戏正确。都错了

如果可能,请在不使用子选择的情况下提供答案。
谢谢。

当您从两个不相关的关系中选择数据时,孩子们加入玩具,孩子们加入游戏,子查询是很自然的方式。由于可能会使用不相关的子查询,因此速度不应该特别慢

如果此查询足够有效,请尝试:

与原始查询相比,它基本上只是颠倒了连接和分组的顺序

SELECT kids.*, t.total_toys, t.toys_bought, t.toys_not_bought,
               g.total_games, g.games_bought, g.games_not_bought
FROM kids
LEFT JOIN (SELECT id_kids_toys,
                  COUNT(*) AS total_toys,
                  SUM(CASE bought WHEN 1 THEN 1 ELSE 0 END) as toys_bought,
                  SUM(CASE bought WHEN -1 THEN 1 ELSE 0 END) as toys_not_bought
           FROM toys
           GROUP BY id_kids_toys) AS t
ON t.id_kids_toys = kids.id_kid
LEFT JOIN (SELECT id_kids_games,
                  COUNT(*) AS total_games,
                  SUM(CASE bought WHEN 1 THEN 1 ELSE 0 END) as games_bought,
                  SUM(CASE bought WHEN -1 THEN 1 ELSE 0 END) as games_not_bought
           FROM games
           GROUP BY id_kids_games) AS g
ON g.id_kids_games = kids.id_kid
ORDER by kids.name;
如果您坚持避免子查询,这可能会大大降低查询的效率:

SELECT kids.*, 
COUNT(DISTINCT toys.id_toy) AS total_toys, 

-- sum only toys joined to first game
SUM(IF(g2.id_game IS NULL AND bought = 1, 1, 0)) AS toys_bought, 
SUM(IF(g2.id_game IS NULL AND bought = -1, 1, 0)) AS toys_not_bought, 

-- sum only games joined to first toy
COUNT(DISTINCT games.id_game) AS total_games, 
SUM(IF(t2.id_toy IS NULL AND bought = 1, 1, 0)) AS games_bought, 
SUM(IF(t2.id_toy IS NULL AND bought = -1, 1, 0)) AS games_not_bought 

FROM kids as k 
LEFT JOIN toys t ON k.id_kid = t.id_kid_toys
LEFT JOIN games g ON k.id_kid = g.id_kid_games

-- select only rows where either game or toy is the first one for this kid
LEFT JOIN toys t2 on k.id_kid = t.id_kid_toys AND t2.id_toy < t.id_toy
LEFT JOIN games g2 ON k.id_kid = g.id_kid_games AND g2.id_game < g.id_game 
WHERE t2.id_toy IS NULL OR g2.id_game IS NULL

GROUP BY k.id_kid
ORDER BY k.name ASC

它的工作原理是确保每个孩子只计算加入第一个玩具的游戏,只计算加入第一个游戏的玩具。

你能发布每个表的样本数据以及表结构吗?@BlueFoots我编辑了这个问题,并试图解释表结构。谢谢。谢谢你。你能发布一些样本数据吗?问题是,除了孩子,玩具和游戏之间没有任何关系,所以你会得到玩具和游戏组合的行。最好的解决方案可能是使用子查询,在加入kid表之前,对每个孩子的玩具和游戏求和。@TerjeD。如果可能的话,我正在寻找一个不使用Subselect的解决方案。我以前用过类似的东西,但速度太慢了:COUNTDISTINCT games 1.id\u games作为total\u games,COUNTDISTINCT games 2.id\u games作为games\u bunded,COUNTDISTINCT games 3.id\u games作为games\u not\u bunded,LEFT JOIN games 1 ON kids.id_kid=games 1.id_kid_games LEFT JOIN games 2 ON kids.id_kid=games 2.id_kid_games AND games 2.Bunded=1 LEFT JOIN games 3 ON kids.id_kid_games AND games 3.Bunded=-1感谢您的回复,但是没有子选择就不可能做到吗?谢谢@Terje!第二个示例不起作用,它为2个玩具和9个游戏提供了38行。第一个示例有效,在0.075秒内获得约800行,这对我来说已经足够了,但不幸的是,它使用了subselect,因此我无法使用它。我尝试改进上一个查询,但仍然不正确。现在我不确定它是否可以在没有子查询的情况下完成。当您能够测试子查询时,使用子查询有什么问题?