7. 7
17.2.1 意図に反した結果 (3/5)
SELECT p.product_id,
COUNT(f.bug_id) AS count_fixed,
COUNT(o.bug_id) AS count_open
FROM BugsProducts p
INNER JOIN Bugs f ON p.bug_id = f.bug_id AND f.status = 'FIXED'
INNER JOIN BugsProducts p2 USING (product_id)
INNER JOIN Bugs o ON p2.bug_id = o.bug_id AND o.status = 'OPEN'
WHERE p.product_id = 1
GROUP BY p.product_id;
FROM BugsProducts p
INNER JOIN Bugs f ON p.bug_id = f.bug_id AND f.status = 'FIXED'
INNER JOIN Bugs o ON p2.bug_id = o.bug_id AND o.status = 'OPEN'
INNER JOIN BugsProducts p2
●BugsProducts p
●Bugs f
➢ bug_idで結合
➢ statusが'FIXED'
●BugsProducts p2
●Bugs o
➢ bug_idで結合
➢ statusが'OPEN'
■ Spaghetti-Query/anti/cartesian.sql
FIXEDとOPEN
のバグの組み合
わせを制限する
条件なし。。。
13. 13
17.5 解決策:分割統治を行う
● 節約の原則(the law of parsimony)
➢
まったく同じ予測をする2つの競合する理論があるとき
は、単純な方が優れている。
● この原則をSQLに当てはめると・・・
➢
まったく同じ結果セットを生む2つのクエリを選択できる
場合は、単純なクエリを選ぶべき。
14. 14
17.5.1 ワンステップずつ (1/2)
SELECT p.product_id, COUNT(f.bug_id) AS count_fixed
FROM BugsProducts p
LEFT OUTER JOIN Bugs f ON p.bug_id = f.bug_id AND f.status = 'FIXED'
WHERE p.product_id = 1
GROUP BY p.product_id;
SELECT p2.product_id, COUNT(o.bug_id) AS count_open
FROM BugsProducts p2
LEFT OUTER JOIN Bugs o ON p2.bug_id = o.bug_id AND o.status = 'OPEN'
WHERE p2.product_id = 1
GROUP BY p2.product_id;
FROM BugsProducts p
INNER JOIN Bugs f ON p.bug_id = f.bug_id AND f.status = 'FIXED'
INNER JOIN Bugs o ON p2.bug_id = o.bug_id AND o.status = 'OPEN'
INNER JOIN BugsProducts p2
SELECT p.product_id,
COUNT(f.bug_id) AS count_fixed,
COUNT(o.bug_id) AS count_open
WHERE p.product_id = 1 GROUP BY p.product_id;
USING (product_id)
デカルト積を避けるために、クエリを分割デカルト積を避けるために、クエリを分割
Spaghetti-Query/anti/cartesian.sql
Spaghetti-Query/soln/split-query.sql
18. 18
17.5.4 上司の問題を解決する
最前の解決策は、上司から求められたタスクを分割して処理すること。
■誰かが取り扱っている製品の数:
SELECT COUNT(*) AS how_many_products FROM Products;
■バグを修正した開発者の数:
SELECT COUNT(DISTINCT assigned_to) AS how_many_developers
FROM Bugs WHERE status = 'FIXED';
■開発者1人あたりの平均バグ修正数:
SELECT AVG(bugs_per_developer) AS average_bugs_per_developer
FROM (SELECT dev.account_id, COUNT(*) AS bugs_per_developer
FROM Bugs b INNER JOIN Accounts dev
ON b.assigned_to = dev.account_id
WHERE b.status = 'FIXED' GROUP BY dev.account_id) t;
■修正したバグの中で顧客から報告されたバグの数:
SELECT COUNT(*) AS how_many_customer_bugs
FROM Bugs b INNER JOIN Accounts cust ON b.reported_by = cust.account_id
WHERE b.status = 'FIXED' AND cust.email NOT LIKE '%@example.com';
19. 19
17.5.5 SQLを用いたSQLの自動的な記述
■ コラム: 複数のUPDATEステートメント生成
【例】 列 last_used の値を、各コンピュータが使用された最新の日付に設定する:
SELECT CONCAT('UPDATE Inventory '
' SET last_used = ''', MAX(u.usage_date), '''',
' WHERE inventory_id = ', u.inventory_id, ';') AS update_statement
FROM ComputerUsage u
GROUP BY u.inventory_id;
● 複雑なSQLクエリを分割すると、データの値によっ
てわずかに異なる、似たようなクエリをいくつも生
成することがある。
➢ それは煩わしいので、「コード生成」を行いましょう。
➢ 「コード生成」は、新しいコードを手で書くには非常に労
力がかかるような場面で効果的。