13. SELECT文の基本構造 (1/2)
employees表
id name birthday gender title
1000 西川 徹 1982/11 M 代表
1010 海老原 雄一郎 1973/11 M
1014 西川 舞 1984/9 F
1018 岡野原 大輔 1982/4 M フェロー
クエリーの結果
SELECT id, name, title id name title
FROM employees 1000 西川 徹 代表
WHERE gender = „M‟; 1010 海老原 雄一郎
1018 岡野原 大輔 フェロー
13
14. SELECT文の基本構造 (2/2)
SELECT id, name, title 選択列リスト
FROM employees FROM句
WHERE gender = „M‟; WHERE句
他にGROUP BY句、HAVING句、 ORDER BY句+ 方言
14
15. 選択列リスト
出力結果を構成する列のリスト
記述できるもの SELECT id, name, title
FROM句の表の列(「*」で全ての列) FROM employees
リテラル値
WHERE gender = „M‟;
式(演算子、関数、スカラー副問合せ)
id name birthday gender title
1000 西川 徹 1982/11 M 代表
1010 海老原 雄一郎 1973/11 M
1014 西川 舞 1984/9 F
1018 岡野原 大輔 1982/4 M フェロー
15
16. FROM句
行を取り出す対象となる表
記述できるもの SELECT id, name, title
表、ビュー、副問合せ FROM employees
JOIN句
WHERE gender = „M‟;
16
17. WHERE句
FROM句の表に適用する抽出条件
記述できるもの SELECT id, name, title
true/false/nullを返す条件式 FROM employees
WHERE gender = „M‟;
id name birthday gender title
1000 西川 徹 1982/11 M 代表
1010 海老原 雄一郎 1973/11 M
1014 西川 舞 1984/9 F
1018 岡野原 大輔 1982/4 M フェロー
17
18. GROUP BY句
グループ関数で集計処理を行う場合の集計単位
SELECT gender,
記述できるもの trunc(age(birthday), 10),
いろいろ記述できるけど、 count(*)
選択列リストのうち、グループ関数を FROM employees
GROUP BY
含まない全ての列を記述するのが
gender,
分かりやすい trunc(age(birthday), 10);
employees表
id name birthday gender
クエリーの結果
gender trunc(…) count(*)
1000 西川 徹 1982/11 M
M 30 1
1010 海老原 雄一郎 1973/11 M
M 20 2
1014 西川 舞 1984/9 F
F 20 1
1018 岡野原 大輔 1982/4 M
18
19. HAVING句
GROUP BY句による集計結果に適用する抽出条件
SELECT gender,
trunc(age(birthday), 10),
記述できるもの count(*)
いろいろ記述できるけど、 FROM employees
GROUP BY
基本的にはグループ関数を含む条件
gender,
(でなければWHERE句に書けばよい)
trunc(age(birthday), 10)
HAVING count(*) = 1;
employees表
id name birthday gender
クエリーの結果
gender trunc(…) count(*)
1000 西川 徹 1982/11 M
M 30 1
1010 海老原 雄一郎 1973/11 M
F 20 1
1014 西川 舞 1984/9 F
1018 岡野原 大輔 1982/4 M
19
20. ORDER BY句
出力結果の構成行の並び順
ORDER BY句を指定しないと、 SELECT id, name, title
並び順は「不定」となる
順序が重要なら必ず指定する
FROM employees
WHERE gender = „M‟
記述できるもの ORDER BY birthday;
選択列リスト中の列
FROM句の表の列
上記に基づく式 ⇒ ex. ORDER BY length(name)
選択列リスト中の列位置 ⇒ ex. ORDER BY 2
20
29. 集合演算の例
employees表
id name birthday gender title
1000 西川 徹 1982/11 M 代表
1010 海老原 雄一郎 1973/11 M
1014 西川 舞 1984/9 F
1018 岡野原 大輔 1982/4 M フェロー
SELECT *
FROM employees
WHERE birthday < '1980/1/1'
UNION
SELECT * id name birthday gender title
FROM employees 1010 海老原 雄一郎 1973/11 M
WHERE gender = 'F' 1014 西川 舞 1984/9 F
ORDER BY id;
29
45. ネステッドループジョイン
二重ループで2つの表にアクセスしながら
結合
外部表/駆動表: 外側のループでアクセスする表
内部表: 内側のループでアクセスする表
for rec1 in “SELECT * FROM employees”
loop
for rec2 in “SELECT * FROM departments WHERE id = :rec1.dept_id”
loop
rec1 + rec2 >> result
end
end
45
46. マージジョイン
2つの表をそれぞれ結合列でソート
それぞれ先頭から順に行を取り出しなが
ら、結合列値が同じ行同士を結合
employees表 departments表
id name dept_id id name
1014 西川 舞 10 10 総務部
1010 海老原 雄一郎 20 20 技術部
1018 岡野原 大輔 20 30 営業部
1000 西川 徹
46
47. ハッシュジョイン
一方の表から取り出した行を元にハッシュテ
ーブルを作成(キーは結合列の値)
もう一方の表から行を取り出し、ハッシュテー
ブルを参照しながら結果を作成
for rec1 in “SELECT * FROM departments”
loop
hash[rec1.id] = rec1
end
for rec2 in “SELECT * FROM employees”
loop
dept = hash[rec2.dept_id]
rec2 + dept >> result
end
47
50. コードの翻訳
性別コード、国コードなど
M→男、F→女
JP→日本、US→アメリカ
employees表 結果
id name gender id name gender
1000 西川 徹 M 1000 西川 徹 男
1010 海老原 雄一郎 M 1010 海老原 雄一郎 男
1014 西川 舞 F 1014 西川 舞 女
1018 岡野原 大輔 M 1018 岡野原 大輔 男
50
51. CASE式
SELECT id, name, 最初の記法は、等号以外の比較
CASE
WHEN gender = 'M' THEN '男'
演算子も利用可能
WHEN gender = 'F' THEN '女'
ELSE 'その他'
END
選択列リスト以外でも利用可能
FROM employees;
SELECT以外のDMLでもOK
SELECT id, name,
UPDATE文で行が持つ値によって更新
CASE gender 値を変えるなど
WHEN 'M' THEN '男' UPDATE … SET col=CASE ...;
WHEN 'F' THEN '女'
ELSE 'その他'
END なお、王道はコードと翻訳語の対
FROM employees; 応表を結合
多言語対応もできる
51
52. CASEの応用 - 横展開(1/2)
SELECT CASE dept_id
WHEN 10 THEN '総務部' WHEN 20 THEN '技術部'
END AS "部門",
sum(CASE gender WHEN 'M' THEN 1 ELSE 0 END) AS "男",
sum(CASE gender WHEN 'F' THEN 1 ELSE 0 END) AS "女"
FROM employees
GROUP BY CASE dept_id
WHEN 10 THEN '総務部' WHEN 20 THEN '技術部'
END;
employees表 結果
id name gender dept_id 部門 男 女
1000 西川 徹 M 1 0
1010 海老原 雄一郎 M 20 総務部 0 1
1014 西川 舞 F 10 技術部 2 0
1018 岡野原 大輔 M 20
52
53. CASEの応用 - 横展開(2/2)
よく分からないので、GROUP BYとsum()を外してみる
SELECT CASE dept_id
WHEN 10 THEN '総務部' WHEN 20 THEN '技術部'
END AS "部門",
CASE gender WHEN 'M' THEN 1 ELSE 0 END AS "男",
CASE gender WHEN 'F' THEN 1 ELSE 0 END AS "女"
FROM employees;
employees表 結果
id name gender dept_id 部門 男 女
1000 西川 徹 M 1 0
1010 海老原 雄一郎 M 20 技術部 1 0
1014 西川 舞 F 10 総務部 0 1
1018 岡野原 大輔 M 20 技術部 1 0
53
54. 「または」
SELECT *
SELECT *
FROM employees
FROM employees
WHERE birthday < '1980/1/1'
WHERE birthday < '1980/1/1'
UNION
OR gender = 'F'
SELECT *
ORDER BY id;
FROM employees
WHERE gender = 'F' id name birthday gender title
ORDER BY id; 1010 海老原 雄一郎 1973/11 M
1014 西川 舞 1984/9 F
以下の条件が全て当てはまる場合は、UNION
のほうが高速かも(オプティマイザ次第)
employees表のレコード数が多い
gender列とbirthday列のそれぞれにインデックスが定義さ
れている
それぞれの条件を満たす行の割合が比較的小さい
54
55. 全体の中で最小値を持つレコード
最年長の社員はだれか?
employees表
id name birthday gender title
1000 西川 徹 1982/11 M 代表
1010 海老原 雄一郎 1973/11 M
1014 西川 舞 1984/9 F
1018 岡野原 大輔 1982/4 M フェロー
id name birthday gender title
1010 海老原 雄一郎 1973/11 M
55
57. WHERE句内の副問合せ
SELECT *
FROM employees
WHERE birthday = (SELECT min(birthday)
FROM employees);
「誕生日が、全社員の中で最も早い誕生日と等
しい人」
副問合せを使った頻出イディオム
birthday列にインデックス
副問合せの最小値を高速に取得
主問合せで誕生日が一致する行を高速に取得
57
58. FROM句内の副問合せ
SELECT *
FROM employees AS e
INNER JOIN (SELECT min(birthday) AS birthday
FROM employees
) AS oldest
ON oldest.birthday = e.birthday;
副問合せの結果は、FROM句内で通常の表と
同じように扱うことができる
「インラインビュー」とも呼ばれる
注意: 副問合せの結果には、インデックスを定義できない!
大きな結果を他の表と結合する場合は、マージジョインかハ
ッシュジョインを使用、ネステッドループジョインなら外部表に
する
58
59. 部分集合毎の最大値を持つレコード
男女別の最年少社員はだれか?
employees表
id name birthday gender title
1000 西川 徹 1982/11 M 代表
1010 海老原 雄一郎 1973/11 M
1014 西川 舞 1984/9 F
1018 岡野原 大輔 1982/4 M フェロー
id name birthday gender title
1000 西川 徹 1982/11 M 代表
1014 西川 舞 1984/9 F
59
60. 相関副問合せ
SELECT *
FROM employees e1
WHERE e1.birthday = (SELECT max(e2.birthday)
FROM employees e2
WHERE e2.gender = e1.gender);
「誕生日が、同性の社員の中で最も遅い誕
生日と等しい人」
相関副問合せの頻出イディオム
gender列+birthday列にインデックス
副問合せの性能最適化
カバリングインデックス
60
61. 行値コンストラクター
SELECT *
FROM employees
WHERE (gender, birthday) IN (SELECT gender, max(birthday)
FROM employees
GROUP BY gender);
「性別と誕生日の組が、『男女別の最も遅い誕
生日』表に含まれる人」
gender列+birthday列にインデックス
オプティマイザによっては、employeesを1行評
価する毎に、副問合せを実行することがある
61
62. EXISTS / NOT EXISTS
SELECT *
FROM employees e1
WHERE NOT EXISTS (
SELECT 1
FROM employees e2
WHERE e2.gender = e1.gender
AND e2.birthday > e1.birthday);
「同姓に自分より後に生まれた人がいない人」
副問合せの選択列リストは、参照されないので何でもいい
副問合せの実行は1行発見したら停止される
gender列+birthday列にインデックス
EXISTS句は「少なくとも1行は存在する」場合に真
最初は理解しづらいが、良い実行計画が得られやすい
62
63. 自己結合 + HAVING (1/2)
SELECT e1.id, e1.name, e1.gender, e1.birthday
FROM employees e1
LEFT OUTER JOIN employees e2
ON e2.gender = e1.gender
AND e2.birthday > e1.birthday
GROUP BY e1.id, e1.name, e1.gender, e1.birthday
HAVING count(e2.id) = 0;
「自分より後に生まれた同姓の人数が0」
自己結合 = 同じ表同士を結合
gender列+birthday列にインデックス
63
64. 自己結合 + HAVING (2/2)
GROUP BYとHAVINGを外し、列を補ってみる。
SELECT e1.id, e1.name, e1.gender, e1.birthday,
e2.id, e2.name, e2.birthday
FROM employees e1
LEFT OUTER JOIN employees e2
ON e2.gender = e1.gender
AND e2.birthday > e1.birthday
e1.id e1.name e1.birthday e1.gender e2.id e2.name e2.birthday
1000 西川 徹 1982/11 M
1010 海老原 雄一郎 1973/11 M 1000 西川 徹 1982/11
1010 海老原 雄一郎 1973/11 M 1018 岡野原 大輔 1982/4
1014 西川 舞 1984/9 F
1018 岡野原 大輔 1982/4 M 1000 西川 徹 1982/11
「count(e2.id)」はe2.idが非NULLの場合のみカウントされる
64