0

子供、おもちゃ、ゲームの3つのテーブル設定があり、それぞれに固有の主キーid_kid、id_toy、id_gameがあります。各子供は複数のおもちゃやゲームを持つことができますが、各おもちゃやゲームは1人の子供だけが所有しています。

おもちゃとゲームには、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

以下のクエリを使用して、子供ごとに、購入したおもちゃと購入していないおもちゃの合計を取得しようとしていますが、結果は2倍になります。

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

1人の子供が2つのおもちゃと4つのゲームを持っていて、すべて購入しました。結果は、合計2つのおもちゃ(正しい)、合計4つのゲーム(正しい)、8つのおもちゃを購入、8つのゲームを購入しました。(両方とも間違っている)

副選択を使用せずに、可能であれば、答えを手伝ってください。ありがとうございました。

4

1 に答える 1

2

2つの無関係な関係(子供がおもちゃに参加している子供と子供がゲームに参加している)からデータを選択しているので、サブクエリはそれを行うための自然な方法です。無相関のサブクエリが使用される可能性があるため、これは特に遅くなることはありません。

このクエリが十分に効率的かどうか試してください。

元のクエリと比較すると、基本的には結合とグループ化の順序が逆になります。

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

これは、子供ごとに、最初のおもちゃに参加したゲームのみがカウントされ、最初のゲームに参加したおもちゃのみがカウントされるようにすることで機能します。

于 2012-11-28T17:45:37.303 に答える