introduction_to_only_sql

70
1 N oSQL入門 201078()Preferred Infrastructure 海老原 雄一郎 当日の録画が以下で公開されていますので、併せてご覧下さい。 http://www.ustream.tv/recorded/8146586

Upload: preferred-infrastructure-preferred-networks

Post on 12-Nov-2014

13.885 views

Category:

Technology


1 download

DESCRIPTION

当日の中継も以下でご覧いただけます。 http://www.ustream.tv/recorded/8146586

TRANSCRIPT

Page 1: introduction_to_only_SQL

1

NoSQL入門

2010年7月8日

(株)Preferred Infrastructure

海老原雄一郎

当日の録画が以下で公開されていますので、併せてご覧下さい。http://www.ustream.tv/recorded/8146586

Page 2: introduction_to_only_SQL

2

NoSQLとは?

NoSQL = only SQLcf. NoSQL = Not only SQL

MS SQLのコマンドラインツール osql とは無関係

SQLが持つ強力な表現力をフル活用すること

そのようなSQLは、見た人が思わず、”Oh!

SQL!”と叫んでしまう

Page 3: introduction_to_only_SQL

3

アジェンダ

1. はじめに

2. SQLのおさらい

3. SQL一発

4. SQLチューニングのポイント

5. ケーススタディ

6. おわりに

Page 4: introduction_to_only_SQL

4

アジェンダ

1. はじめに

2. SQLのおさらい

3. SQL一発

4. SQLチューニングのポイント

5. ケーススタディ

6. おわりに

Page 5: introduction_to_only_SQL

5

自己紹介

海老原雄一郎 (@yebihara)RDBMSを愛しているソフトウェアエンジニア

日本オラクルでテクニカルコンサルタントを6年データベースの基礎をみっちり

ソニーでPostgreSQLとMySQLOSS DBを社内に広めるプロジェクト「現場で使えるMySQL」などで有名な松信さんは当時のチームメンバー

DBセキュリティ製品のアイピーロックスでデータベース三昧の日々

Oracle, MS SQL Server, IBM DB2, Sybase ASE, 日立HiRDB, PostgreSQL

現在: PFIとあるシステム開発で久々にMySQLをみっちり

Page 6: introduction_to_only_SQL

6

今日の目的

SQLの表現力の強力さを理解する

SQLで効率的にプログラミングを減らす手法を学ぶ

性能の良いSQLを書けそうな気になる

Page 7: introduction_to_only_SQL

7

主な対象者

リレーショナルデータベースをちょっとは触ったことがある

シンプルなSQL文なら書けるSELECT ~ FROM~WHERE~

ジョインとかGROUP BYとかはあんまり

Page 8: introduction_to_only_SQL

8

アジェンダ

1. はじめに

2. SQLのおさらい

3. SQL一発

4. SQLチューニングのポイント

5. ケーススタディ

6. おわりに

Page 9: introduction_to_only_SQL

9

SQLの特徴 (1/2)

主にリレーショナルデータベースを操作するためのクエリー言語データは行と列で構成される表に格納されている表=table,テーブル行=row,レコード列=column, カラム

検索結果も表として得られる表=集合、要素は順序付けられていない

id name birthday gender title

1000 西川徹 1982/11 M 代表

1010 海老原雄一郎 1973/11 M

1014 西川舞 1984/9 F

1018 岡野原大輔 1982/4 M フェロー

employees表

列 列 列 列 列

Page 10: introduction_to_only_SQL

10

SQLの特徴 (2/2)

宣言型プログラミング言語「どのようにデータを処理するか?」ではなく、「どのような処理結果(データの集合)が欲しいか?」を記述する

クエリーオプティマイザーSQLを翻訳して、実行計画を作成する実行計画= 「どのようにデータを処理するか?」プランナーと呼ばれることも

タクシーのメタファータクシーで行き先を告げると、運ちゃん(オプティマイザー)が道順(実行計画)を考えて、連れて行ってくれる自分で車を運転する場合は、自分で道順を考えないといけない→ 手続き型プログラミング

Page 11: introduction_to_only_SQL

11

標準SQL

ISOで標準化されているSQL-92, SQL:1999, SQL:2003, SQL:2008

全てを実装しているRDBMSは存在しない

RDBMS固有の拡張(方言)も多い

Page 12: introduction_to_only_SQL

12

SQLの種類

DML(Data Manipulation Language)データの中身を操作するSELECT、INSERT、UPDATE、DELETEなど標準SQLの範囲内で結構いける

DDL(Data Definition Language)DBオブジェクト(表や索引など)の定義情報を操作するCREATE TABLEなど主要部分は標準SQLでOK、ただし方言の利用もほぼ必須

その他トランザクション制御権限管理

Page 13: introduction_to_only_SQL

13

SELECT文の基本構造 (1/2)

SELECT id, name, title

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 フェロー

employees表

id name title

1000 西川徹 代表

1010 海老原雄一郎

1018 岡野原大輔 フェロー

クエリーの結果

Page 14: introduction_to_only_SQL

14

SELECT文の基本構造 (2/2)

SELECT id, name, title

FROM employees

WHERE gender = „M‟;

選択列リスト

他にGROUP BY句、HAVING句、 ORDER BY句+方言

FROM句

WHERE句

Page 15: introduction_to_only_SQL

15

選択列リスト

id name birthday gender title

1000 西川徹 1982/11 M 代表

1010 海老原雄一郎 1973/11 M

1014 西川舞 1984/9 F

1018 岡野原大輔 1982/4 M フェロー

出力結果を構成する列のリスト

記述できるものFROM句の表の列(「*」で全ての列)

リテラル値

式(演算子、関数、スカラー副問合せ)

SELECT id, name, title

FROM employees

WHERE gender = „M‟;

Page 16: introduction_to_only_SQL

16

FROM句

行を取り出す対象となる表

記述できるもの表、ビュー、副問合せ

JOIN句

SELECT id, name, title

FROM employees

WHERE gender = „M‟;

Page 17: introduction_to_only_SQL

17

WHERE句

id name birthday gender title

1000 西川徹 1982/11 M 代表

1010 海老原雄一郎 1973/11 M

1014 西川舞 1984/9 F

1018 岡野原大輔 1982/4 M フェロー

FROM句の表に適用する抽出条件

記述できるものtrue/false/nullを返す条件式

SELECT id, name, title

FROM employees

WHERE gender = „M‟;

Page 18: introduction_to_only_SQL

18

GROUP BY句

gender trunc(…) count(*)

M 30 1

M 20 2

F 20 1

クエリーの結果

SELECT gender,trunc(age(birthday), 10),count(*)

FROM employeesGROUP BY

gender,trunc(age(birthday), 10);

グループ関数で集計処理を行う場合の集計単位

記述できるものいろいろ記述できるけど、

選択列リストのうち、グループ関数を含まない全ての列を記述するのが分かりやすい

id name birthday gender

1000 西川徹 1982/11 M

1010 海老原雄一郎 1973/11 M

1014 西川舞 1984/9 F

1018 岡野原大輔 1982/4 M

employees表

Page 19: introduction_to_only_SQL

19

HAVING句

gender trunc(…) count(*)

M 30 1

F 20 1

クエリーの結果

SELECT gender,trunc(age(birthday), 10),count(*)

FROM employeesGROUP BY

gender,trunc(age(birthday), 10)

HAVING count(*) = 1;

GROUP BY句による集計結果に適用する抽出条件

記述できるものいろいろ記述できるけど、

基本的にはグループ関数を含む条件(でなければWHERE句に書けばよい)

id name birthday gender

1000 西川徹 1982/11 M

1010 海老原雄一郎 1973/11 M

1014 西川舞 1984/9 F

1018 岡野原大輔 1982/4 M

employees表

Page 20: introduction_to_only_SQL

20

ORDER BY句

出力結果の構成行の並び順ORDER BY句を指定しないと、並び順は「不定」となる

順序が重要なら必ず指定する

記述できるもの選択列リスト中の列

FROM句の表の列

上記に基づく式⇒ ex. ORDER BY length(name)

選択列リスト中の列位置⇒ ex. ORDER BY 2

SELECT id, name, title

FROM employees

WHERE gender = „M‟

ORDER BY birthday;

Page 21: introduction_to_only_SQL

21

結合(ジョイン)

2つの表から取得した行を連結して、1つの表を構成する操作その結果をさらに別の表と結合することもできる

いろいろな結合直積(デカルト積、クロスジョイン)

等価結合

内部結合

外部結合

半結合

逆結合

Page 22: introduction_to_only_SQL

22

等価結合

id name dept_id

1000 西川徹

1010 海老原雄一郎 20

1014 西川舞 10

1018 岡野原大輔 20

employees表

id name

10 総務部

20 技術部

30 営業部

departments表

emp.id emp.name dept.name

1010 海老原雄一郎 技術部

1014 西川舞 総務部

1018 岡野原大輔 技術部

結果

SELECT emp.id, emp.name, dept.name

FROM employees emp,

departments dept

WHERE dept.id = emp.dept_id;

Page 23: introduction_to_only_SQL

23

直積

id name dept_id

1000 西川徹

1010 海老原雄一郎 20

1014 西川舞 10

1018 岡野原大輔 20

employees表

emp.id emp.name dept.name

1000 西川徹 総務部

1000 西川徹 技術部

1000 西川徹 営業部

1010 海老原雄一郎 総務部

1010 海老原雄一郎 技術部

1010 海老原雄一郎 営業部

1014 西川舞 総務部

1014 西川舞 技術部

1014 西川舞 営業部

1018 岡野原大輔 総務部

1018 岡野原大輔 技術部

1018 岡野原大輔 営業部

結果

SELECT emp.id, emp.name, dept.name

FROM employees emp,

departments dept;

id name

10 総務部

20 技術部

30 営業部

departments表

Page 24: introduction_to_only_SQL

24

直積から結合を理解する

emp.id emp.name emp.dept_id dept.id dept.name

1000 西川徹 10 総務部

1000 西川徹 20 技術部

1000 西川徹 30 営業部

1010 海老原雄一郎 20 10 総務部

1010 海老原雄一郎 20 20 技術部

1010 海老原雄一郎 20 30 営業部

1014 西川舞 10 10 総務部

1014 西川舞 10 20 技術部

1014 西川舞 10 30 営業部

1018 岡野原大輔 20 10 総務部

1018 岡野原大輔 20 20 技術部

1018 岡野原大輔 20 30 営業部

SELECT emp.id, emp.name,

dept.name

FROM employees emp,

departments dept

WHERE dept.id = emp.dept_id;

emp.id emp.name dept.name

1010 海老原雄一郎 技術部

1014 西川舞 総務部

1018 岡野原大輔 技術部

Page 25: introduction_to_only_SQL

25

JOIN構文

FROM句に表をカンマ区切りで並べて、結合条件をWHERE句に書くのは古い記法

JOIN構文

SELECT emp.id, emp.name, dept.name

FROM employees emp

INNER JOIN departments dept ON dept.id = emp.dept_id;

SELECT emp.id, emp.name, dept.name

FROM employees emp

CROSS JOIN departments dept;

Page 26: introduction_to_only_SQL

26

左外部結合

id name dept_id

1000 西川徹

1010 海老原雄一郎 20

1014 西川舞 10

1018 岡野原大輔 20

employees表

emp.id emp.name dept.name

1000 西川徹

1010 海老原雄一郎 技術部

1014 西川舞 総務部

1018 岡野原大輔 技術部

結果

SELECT emp.id, emp.name, dept.name

FROM employees emp

LEFT OUTER JOIN departments dept

ON dept.id = emp.dept_id;

どの部門にも所属していない人も出力したい。

id name

10 総務部

20 技術部

30 営業部

departments表

Page 27: introduction_to_only_SQL

27

完全外部結合

id name dept_id

1000 西川徹

1010 海老原雄一郎 20

1014 西川舞 10

1018 岡野原大輔 20

employees表

emp.id emp.name dept.name

1000 西川徹

1010 海老原雄一郎 技術部

1014 西川舞 総務部

1018 岡野原大輔 技術部

営業部

結果

SELECT emp.id, emp.name, dept.name

FROM employees emp

FULL OUTER JOIN departments dept

ON dept.id = emp.dept_id;

どの部門にも所属していない人も出力したい。誰も所属していない部門も出力したい。

id name

10 総務部

20 技術部

30 営業部

departments表

Page 28: introduction_to_only_SQL

28

集合演算

選択列リストの構造が等しい2つのSELECT文の結果(集合)の演算を行う

UNION(和) – どちらかに含まれるINTERSECT(積) – どちらにも含まれるEXCEPT(差) – 1つ目に含まれて、2つ目には含まれない

通常は、重複行(全ての列の値が同じ行)は1行として戻される

UNION ALLのようにALLキーワードを指定すると、重複はそのまま保持される集合的ではないけど便利重複が発生しないことが保証される場合にALLを使用すると性能Up

Page 29: introduction_to_only_SQL

29

集合演算の例

SELECT *

FROM employees

WHERE birthday < '1980/1/1'

UNION

SELECT *

FROM employees

WHERE gender = 'F'

ORDER BY id;

id name birthday gender title

1010 海老原雄一郎 1973/11 M

1014 西川舞 1984/9 F

id name birthday gender title

1000 西川徹 1982/11 M 代表

1010 海老原雄一郎 1973/11 M

1014 西川舞 1984/9 F

1018 岡野原大輔 1982/4 M フェロー

employees表

Page 30: introduction_to_only_SQL

30

アジェンダ

1. はじめに

2. SQLのおさらい

3. SQL一発

4. SQLチューニングのポイント

5. ケーススタディ

6. おわりに

Page 31: introduction_to_only_SQL

31

SQL一発

SQLーーーッ!!!

イッパーーーッツ!!!

Page 32: introduction_to_only_SQL

32

「SQL一発」とは?

MLや掲示板でよく見かける質問「・・・な結果をSQL一発で取得できますか?」

テーブルから単純に行と列を抽出するだけでは欲しい結果イメージが得られない

アプリケーション側で検索結果を加工すればいいのでは?コード書くのが面倒

ツールの制限SQLクエリーを受け入れる帳票ツールなど

Page 33: introduction_to_only_SQL

33

SQL一発 ノススメ

SQLを工夫すれば実際に何とかなるケースも多い

SQLはとても表現力が高い

アプリケーションコードが簡潔になる開発生産性の向上

バグが入り込む余地が少なくなる

まさに NoSQL!

Page 34: introduction_to_only_SQL

34

SQL一発のデメリット その1

トリッキーで理解しづらいSQLになりやすい保守性の低下SQLの意味を、書いた人しか理解できない

1か月経てば、書いた本人ですら忘れる

解決策コードレビュー時に、 SQLの意図を日本語(自然言語)で事細かに説明させる「よく分からないけど、試してみたらうまくいく」はダメ

その説明をソースコード内のSQLのそばにコメントで残しておく記法の標準化長く複雑なSQLをできるだけ見易くする改行、インデントルール大文字、小文字の使い分け

Page 35: introduction_to_only_SQL

35

SQL一発のデメリット その2

SQLの実行性能が悪くなりやすいオプティマイザーの限界

道を知らないタクシーの運ちゃん

道は知っているけど、渋滞情報を知らない運ちゃん

解決策レスポンス要求がシビアな時は使わない

脳内オプティマイズ初めに理想の実行計画を頭の中で考える実行計画を確認しながら、クエリーを書くタクシーの運ちゃんに道順を指定する

Page 36: introduction_to_only_SQL

36

あれ?宣言的プログラミングは・・・

1回限りのアドホック処理、事前検証、プロトタイピング、開発初期で活用しよう

リリース前には、性能要件、データ量、値の偏り、実行頻度などを考慮して、実行計画をチューニング

宣言的な世界と手続き的な世界を行ったり来たりもし同じ処理を手続き型で実現するとしたら、どのようなアルゴリズムを用いるか?

Page 37: introduction_to_only_SQL

37

アジェンダ

1. はじめに

2. SQLのおさらい

3. SQL一発

4. SQLチューニングのポイント

5. ケーススタディ

6. おわりに

Page 38: introduction_to_only_SQL

38

重要なのは実行計画

速いSQL=適切な実行計画

実行計画を立てるのはオプティマイザ

オプティマイザの動作を理解することが重要

Page 39: introduction_to_only_SQL

39

オプティマイザが考慮すること

SQLの字面

表の定義情報特にインデックスクラスター、パーティショニング

ハードウェアリソースCPU、メモリ、I/O

統計情報行数、ページ数列毎の値の分布定期的に更新する必要性

Page 40: introduction_to_only_SQL

40

実行計画の主な構成要素

表へのアクセスなるべく速く(特に索引)

なるべく回数を少なく

なるべく早めに絞り込み

結合アルゴリズム

ソート、集計なるべく回数を少なく

なるべく小さく

Page 41: introduction_to_only_SQL

41

索引(インデックス)

普通はB+Tree特定の列値を持つレコードを探すほか、最大値・最小値の取得、行数のカウント、行のソートにも利用される部分インデックス、関数インデックス、リバースキーインデックス、クラスターインデックスなどのバリエーション複合(マルチカラム)インデックスの場合、WHERE句で=条件が適用される列を先頭にするWHERE句で索引列を演算しない(関数インデックスを除く)

B+Tree以外にもハッシュインデックス、ビットマップインデックスなどあまり多く定義すると更新性能が悪化する

日本語Wikipedia「B+木」より引用

floor(score) = 5 score >= 5 AND score < 6

Page 42: introduction_to_only_SQL

42

表へのアクセス方法 (1/2)

フルテーブルスキャンテーブルの物理領域を先頭から最後まで探索

大きいと時間がかかる。けど、O(n)なので予測しやすい

マルチブロックリードや先読みによる効率化も可能

削除済みの領域に注意

インデックスユニークスキャンユニークなB+Treeインデックスをルートからリーフまで辿り、取得した行アドレス(0 or 1)にアクセス

ユニークキー(主キー含む)列に対する等号(=)によるWHERE条件

速い

Page 43: introduction_to_only_SQL

43

表へのアクセス方法 (2/2)

インデックスレンジスキャンB+Treeインデックスをルートからリーフまで辿り、さらにそこから右に辿りながら取得した全ての行アドレス(0以上)にアクセス

非ユニークインデックスの定義列に対する等号によるWHERE条件

インデックス定義列に対する不等号、前方一致のLIKEによるWHERE条件

取得行の割合が多いと、テーブルフルスキャンのほうが有利

フルインデックススキャンB+Treeのリーフを左端から右端まで全て探索→ ソート処理なしでソートされた結果

または、B+Treeインデックスの物理領域を先頭から最後まで探索

参照列が全てインデックスに含まれる場合、テーブルフルスキャンよりもI/O量が少なくなる

Page 44: introduction_to_only_SQL

44

結合アルゴリズム

ネステッドループジョインレスポンスタイム重視省メモリ

ハッシュジョイン大量データ向き、スループット重視結合条件が等号(=)データ量に応じたメモリが必要

マージジョイン大量データ向き、スループット重視結合条件が等号以外(>, >=, <, <=など)メモリが多ければ性能が良くなる

Page 45: introduction_to_only_SQL

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

Page 46: introduction_to_only_SQL

46

マージジョイン

2つの表をそれぞれ結合列でソート

それぞれ先頭から順に行を取り出しながら、結合列値が同じ行同士を結合

id name dept_id

1014 西川舞 10

1010 海老原雄一郎 20

1018 岡野原大輔 20

1000 西川徹

employees表

id name

10 総務部

20 技術部

30 営業部

departments表

Page 47: introduction_to_only_SQL

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

Page 48: introduction_to_only_SQL

48

オプティマイザに言うことを聞かせる

オプティマイザが思った通りの実行計画を立ててくれないことがある

クエリーの書き換え

ヒントクエリーの中に明示的なヒントを埋め込む

オプティマイザパラメータのチューニングコストの重み付けの変更特定のアルゴリズムの使用許可・不許可

統計情報の固定化あえて統計情報を古いままにする

全てDBMS

依存

Page 49: introduction_to_only_SQL

49

アジェンダ

1. はじめに

2. SQLのおさらい

3. SQL一発

4. SQLチューニングのポイント

5. ケーススタディ

6. おわりに

Page 50: introduction_to_only_SQL

50

コードの翻訳

性別コード、国コードなどM→男、F→女

JP→日本、US→アメリカ

id name gender

1000 西川徹 M

1010 海老原雄一郎 M

1014 西川舞 F

1018 岡野原大輔 M

employees表id name gender

1000 西川徹 男

1010 海老原雄一郎 男

1014 西川舞 女

1018 岡野原大輔 男

結果

Page 51: introduction_to_only_SQL

51

CASE式

SELECT id, name,

CASE

WHEN gender = 'M' THEN '男'

WHEN gender = 'F' THEN '女'

ELSE 'その他'

END

FROM employees;

SELECT id, name,

CASE gender

WHEN 'M' THEN '男'

WHEN 'F' THEN '女'

ELSE 'その他'

END

FROM employees;

最初の記法は、等号以外の比較演算子も利用可能

選択列リスト以外でも利用可能

SELECT以外のDMLでもOKUPDATE文で行が持つ値によって更新値を変えるなどUPDATE … SET col=CASE ...;

なお、王道はコードと翻訳語の対応表を結合多言語対応もできる

Page 52: introduction_to_only_SQL

52

CASEの応用 -横展開(1/2)

id name gender dept_id

1000 西川徹 M

1010 海老原雄一郎 M 20

1014 西川舞 F 10

1018 岡野原大輔 M 20

employees表

部門 男 女

1 0

総務部 0 1

技術部 2 0

結果

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;

Page 53: introduction_to_only_SQL

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;

部門 男 女

1 0

技術部 1 0

総務部 0 1

技術部 1 0

結果id name gender dept_id

1000 西川徹 M

1010 海老原雄一郎 M 20

1014 西川舞 F 10

1018 岡野原大輔 M 20

employees表

Page 54: introduction_to_only_SQL

54

「または」

以下の条件が全て当てはまる場合は、UNIONのほうが高速かも(オプティマイザ次第)

employees表のレコード数が多いgender列とbirthday列のそれぞれにインデックスが定義されているそれぞれの条件を満たす行の割合が比較的小さい

SELECT *

FROM employees

WHERE birthday < '1980/1/1'

UNION

SELECT *

FROM employees

WHERE gender = 'F'

ORDER BY id;

SELECT *

FROM employees

WHERE birthday < '1980/1/1'

OR gender = 'F'

ORDER BY id;

id name birthday gender title

1010 海老原雄一郎 1973/11 M

1014 西川舞 1984/9 F

Page 55: introduction_to_only_SQL

55

全体の中で最小値を持つレコード

最年長の社員はだれか?

id name birthday gender title

1000 西川徹 1982/11 M 代表

1010 海老原雄一郎 1973/11 M

1014 西川舞 1984/9 F

1018 岡野原大輔 1982/4 M フェロー

employees表

id name birthday gender title

1010 海老原雄一郎 1973/11 M

Page 56: introduction_to_only_SQL

56

グループ関数の限界

「最年長社員の誕生日」だけなら簡単

しかし、最年長社員の社員番号(id)や名前(name)は、グループ関数では取得できない

SELECT min(birthday)

FROM employees;

Page 57: introduction_to_only_SQL

57

WHERE句内の副問合せ

「誕生日が、全社員の中で最も早い誕生日と等しい人」

副問合せを使った頻出イディオム

birthday列にインデックス副問合せの最小値を高速に取得

主問合せで誕生日が一致する行を高速に取得

SELECT *

FROM employees

WHERE birthday = (SELECT min(birthday)

FROM employees);

Page 58: introduction_to_only_SQL

58

FROM句内の副問合せ

副問合せの結果は、FROM句内で通常の表と同じように扱うことができる「インラインビュー」とも呼ばれる

注意:副問合せの結果には、インデックスを定義できない!

大きな結果を他の表と結合する場合は、マージジョインかハッシュジョインを使用、ネステッドループジョインなら外部表にする

SELECT *

FROM employees AS e

INNER JOIN (SELECT min(birthday) AS birthday

FROM employees

) AS oldest

ON oldest.birthday = e.birthday;

Page 59: introduction_to_only_SQL

59

部分集合毎の最大値を持つレコード

男女別の最年少社員はだれか?

id name birthday gender title

1000 西川徹 1982/11 M 代表

1010 海老原雄一郎 1973/11 M

1014 西川舞 1984/9 F

1018 岡野原大輔 1982/4 M フェロー

employees表

id name birthday gender title

1000 西川徹 1982/11 M 代表

1014 西川舞 1984/9 F

Page 60: introduction_to_only_SQL

60

相関副問合せ

「誕生日が、同性の社員の中で最も遅い誕生日と等しい人」

相関副問合せの頻出イディオム

gender列+birthday列にインデックス副問合せの性能最適化

カバリングインデックス

SELECT *

FROM employees e1

WHERE e1.birthday = (SELECT max(e2.birthday)

FROM employees e2

WHERE e2.gender = e1.gender);

Page 61: introduction_to_only_SQL

61

行値コンストラクターSELECT *

FROM employees

WHERE (gender, birthday) IN (SELECT gender, max(birthday)

FROM employees

GROUP BY gender);

「性別と誕生日の組が、『男女別の最も遅い誕生日』表に含まれる人」

gender列+birthday列にインデックス

オプティマイザによっては、employeesを1行評価する毎に、副問合せを実行することがある

Page 62: introduction_to_only_SQL

62

EXISTS / NOT EXISTSSELECT *

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行は存在する」場合に真最初は理解しづらいが、良い実行計画が得られやすい

Page 63: introduction_to_only_SQL

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列にインデックス

Page 64: introduction_to_only_SQL

64

自己結合 + HAVING (2/2)

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

GROUP BYとHAVINGを外し、列を補ってみる。

「count(e2.id)」はe2.idが非NULLの場合のみカウントされる

Page 65: introduction_to_only_SQL

65

アジェンダ

1. はじめに

2. SQLのおさらい

3. SQL一発

4. SQLチューニングのポイント

5. ケーススタディ

6. おわりに

Page 66: introduction_to_only_SQL

66

今日の目的 (再掲)

SQLの表現力の強力さを理解する

SQLで効率的にプログラミングを減らす手法を学ぶ

性能の良いSQLを書けそうな気になる

Page 67: introduction_to_only_SQL

67

今日話せなかったこと (1/2)

実行計画の確認方法MySQL

漢のコンピュータ道 - MySQLの EXPLAINを徹底解説!! -http://nippondanji.blogspot.com/2009/03/mysqlexplain.html

「エキスパートのためのMySQL[運用+管理]トラブルシューティングガイド」

PostgreSQL

Explaining Explain ~ PostgreSQLの実行計画を読む~http://lets.postgresql.jp/documents/technical/query_tuning/

Oracle

Web、書店でいくらでも情報は見つかる

究極はマニュアルセットの「パフォーマンス・チューニング・ガイド」Oracle Technology Networkで公開されているhttp://www.oracle.com/technology/global/jp/index.html

Page 68: introduction_to_only_SQL

68

今日話せなかったこと (2/2)

最近のSQL共通表式(WITH句)OLAP関数ウィンドウ関数

RDBMS依存のSQLチューニング手法

データベースの論理設計テーブル設計がきれいだと、良い性能が得られやすい逆に、性能重視であえて非正規化することも

ORマッパーを介したDBアクセス

昨今のデータベース界隈の状況

Page 69: introduction_to_only_SQL

69

まとめ

SQLは複雑な処理を簡潔に記述できる

SQLは同じ結果を得るのに、様々な書き方ができる

SQLの書き方によって性能に大きな違いが出ることがある

SQLチューニングによって性能を最適化できる

Page 70: introduction_to_only_SQL

70

Q&A