2012-11-28 73 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