5. 5
7.2.1 デメリット : 値の検索が大変
✔ performance と printing の両方が付いてるのを探す
✔ やっぱり大変。
bug_id description tag1 tag2 tag3
1234 保存処理でクラッシュする crash NULL NULL
3456 パフォーマンスの向上 printing performance NULL
SELECT * FROM Bugs
WHERE (tag1 = 'performance' OR tag2 = 'performance' OR tag3 = 'performance')
AND (tag2 = 'printing' OR tag2 = 'printing' OR tag3 = 'printing')
クエリで取得できる列
6. 6
7.2.1 デメリット : IN で少しだけ楽に
✔ 複数タグ条件の検索は IN を使うと多少は短くなる
✔ 普通のINは tag1 IN ('performance', 'printing')
『列名 IN (値1, 値2, ...)』 なので珍しい書式。
SELECT * FROM Bugs
WHERE 'performance' IN (tag1, tag2, tag3)
AND 'printing' IN (tag1, tag2, tag3);
1つの値に対して、複数の列を検索する時点で危険な兆候では?
7. 7
7.2.2 デメリット : 値の追加が大変
✔ 値の追加を行うためには、先に空き列を確認する必要。
✔ アトミックに実行しないと不整合発生 (ロックが必要)
SELECT * FROM Bugs Where bug_id = 3456;
UPDATE Bugs SET tag2 = 'performance' WHERE bug_id = 3456;
bug_id description tag1 tag2 tag3
1234 保存処理でクラッシュする crash NULL NULL
3456 パフォーマンスの向上 printing NULL NULL
① SELECT で対象バグを検索
“performance”
② NULL列に値を設定
9. 9
7.2.2 一番先頭の NULL 列に値を入れたい
✔ COALESCE (こーあれす:合体) 関数 を使う。
✔ COALESCEは NULL でない最初の引数を返す関数。
UPDATE Bugs
SET tag1 = CASE
WHEN 'performance' IN (tag2, tag3) THEN tag1
ELSE COALESCE(tag1, 'performance') END,
tag2 = CASE
WHEN 'performance' IN (tag1, tag3) THEN tag2
ELSE COALESCE(tag2, 'performance') END,
tag3 = CASE
WHEN 'performance' IN (tag1, tag2) THEN tag3
ELSE COALSCE(tag3, 'performance') END
WHERE bug_id = 3456;
tag2, tag3に既に'performance'が入っている場合はそのままにして、
最初にNULL以外が来る場合、すなわちtag1がNULLの場合は、
値 'perfoamance' が設定される。
19. 19
7.5 あるタグを付けられたバグを検索
SELECT * FROM Bugs
WHERE tag1 = 'performance'
OR tag2 = 'performance'
OR tag3 = 'performance'
✔ before
✔ after
SELECT * FROM Bugs JOIN Tags USING (bug_id)
WHERE tag = 'performance';
20. 20
7.5 複数タグが付与されているバグを検索する
✔ before
✔ after
SELECT * FROM Bugs
INNER JOIN Tags AS t1 USING (bug_id)
INNER JOIN Tags AS t2 USING (bug_id)
WHERE t1.tag = 'printing' AND t2.tag = 'performance';
SELECT * FROM Bugs
WHERE (tag1 = 'performance' OR tag2 = 'performance' OR tag3 = 'performance')
AND (tag2 = 'printing' OR tag2 = 'printing' OR tag3 = 'printing')
afterは管理タグ数が増えても、クエリが変わらないのがポイント。
21. 21
7.5 値の追加も簡単に
✔ before
SELECT * FROM Bugs Where bug_id = 3456;
UPDATE Bugs SET tag2 = 'performance' WHERE bug_id = 3456;
✔ after
INSERT INTO Tags (bugs_id, tag) VALUES (1234, 'save');
22. 22
7.5 値の削除も簡単に
✔ before
UPDATE Bugs
SET tag1 = NULLIF(tag1, 'performance'),
tag2 = NULLIF(tag2, 'performance'),
tag3 = NULLIF(tag3, 'performance')
WHERE bug_id = 3456;
✔ after
DELETE FROM Tags WHERE bug_id = 3456 AND tag = 'performance';
39. 39
8.2.5 テーブルをまたいだクエリ実行
✔ すべての未完了のバグの合計を算出したい場合
✔ UNION が必要
● 同じ列を持つSELECT結果を結合する
● 重複列は DISTINCT を付けていなくても削除される
SELECT b.status, COUNT(*) AS count_per_status FROM (
SELECT * FROM Bugs_2008
UNION
SELECT * FROM Bugs_2009
UNION
SELECT * FROM Bugs_2010 ) AS b
GROUP BY b.status;
40. 40
8.2.6 メタデータの同期
✔ バグ修正時間を記録することにした。
✔ 1つのALTER TABLEでは、2010年しか変更されない。
✔ 年ごとに列構造が違うと UNION できない。
● UNION できる条件は、列が全て同じであること。
ALTER TABLE Bugs_2010 ADD COLUMN hours NUMERIC(9, 2);
59. 59
9.2.2 SQLでのFLOATの使用
✔ データベースによっては丸めた値を表示する。
SELECT hourly_date FROM Accounts WHERE account_id = 123;
結果 : 59.95
✔ 10億倍すると、差異が見えてくる。
SELECT hourly_date * 1000000000
FROM Accounts WHERE account_id = 123;
結果 : 59950000762.939
60. 60
9.2.2 値の比較が難しい
✔ 問題 : 誤差により、イコールにならない。
SELECT * FROM Accounts WHERE hourly_rate = 59.95;
結果 : 一致する行はありません。
✔ 対策 : 精度に応じて差分を認める判定を行う。
SELECT * FROM Accounts
WHERE ABS(hourly_rate – 59.95) < 0.000001;
結果 : 該当行あり。
100万分の1の誤差は等価と見なす。
61. 61
9.2.2 集約計算を行うと、誤差は拡大する
✔ 1つずつ小さな誤差でも、SUMで繰り返すと大きくなる。
SELECT SUM (b.hours * a.hourly_rate) AS project_cost
FROM Bugs AS b
INNER JOIN Accounts AS a ON b.assigned_to = a.account_id;
✔ 掛け算による集約計算は、より誤差を大きくする。
● 1に1000回 1 を掛けても、結果は 1 。
● 1に1000回 0.999 を掛けると、結果は 約0.3677。