2012-11-28 43 views
0

我有一个三表设置:孩子们,玩具和游戏,每个都有独特的主键:id_kid,id_toy和id_game。每个孩子可以有多个玩具和游戏,但每个玩具或游戏只有一个孩子。MySQL使用总和和左连接加倍结果

的玩具和游戏有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款游戏。 (都是错误的)

请帮忙做出答案 - 如果可能的话 - 不使用子查询。 谢谢。

+0

你可以发布每个表的样本数据以及表结构吗? – Taryn

+0

@bluefeet我编辑了这个问题,并试图解释表结构。谢谢。 – noru

+0

谢谢你,你可以发布一些样本数据? – Taryn

回答

2

当你从两个不相关的关系中选择数据时(孩子们加入到玩具中,孩子们加入到游戏中),子查询是这样做的自然方式。由于可能使用不相关的子查询,因此这不应该特别慢。

尝试,如果该查询是充分有效:

相比于原始查询,它基本上只是反转joinining和分组的顺序。

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 

它可以通过确保每个孩子,只有游戏加入到第一个玩具是计数,只有加入到第一场比赛的玩具才会被计数。

+0

感谢您的回应,但是如果没有子选择,不可能做到这一点吗? – noru

+0

检查我的答案更新。 –

+0

谢谢@Terje!第二个例子不起作用,它给了38行2个玩具和9个游戏。第一个例子工作,在0.075秒内获得约800行,这对我来说足够好,但不幸的是它使用了子查询,所以我不能使用它。 – noru