SQL を書こう
●
なかじまゆうじ
08/08/2014 2
はじめに
●
この資料は SQL の構文を覚えた新人向けに、「っでどう
やって書くの?」を説明するために作りました。
●
対象読者は、 SQL は読めるけど書けない人。一応、新人
想定。
●
なかじまの偏見に満ち満ちているので、間違っているこ
ともあるかもです。
●
一部、 SQL Server を前提としている部分があります。
08/08/2014 3
SQL とは
●
SQL は、集合論をベースとする集合指向かつ宣言型の言
語です。 Java や C# の様な手続き型の言語ではありませ
んので、コンピュータに対して「手順」を示すことはし
ません。「何が欲しいか」をコンピュータに提示しま
す。
●
集合論なので本来はベン図とかが登場するところです
が、そこから SQL 文にたどり着くのは難しいので、まず
は Excel のシートを思い出しましょう。
●
join したらシートに列が増えていく、絞り込んだら行
が減っていく感じです。
08/08/2014 4
SQL の基本は
1. 集めて繋いで
2. 絞って
3. 加工して
4. 表示する。
08/08/2014 5
SQL の基本は
1. 集めて繋いで
2. 絞って
3. 加工して
4. 表示する。
Select COL_1, COL_2
From TAB_1
Inner join TAB_2 On ...
Where COL_3 = 'x'
Order by COL_1
08/08/2014 6
まず集める
●
必要なテーブルを集めましょう。
●
取得したいデータがあるテーブル
●
絞り込み条件になるデータがあるテーブル
●
それらを繋ぐために必要なテーブル
08/08/2014 7
そして繋ぐ
●
join しましょう。
●
join するテーブルのそれぞれに、対応するデータが必
ずある場合は inner join を使います。
●
join するテーブルの片方に対応するデータが無いかも
しれない場合でも、そのデータが必要ない(両方に対
応するデータがある場合だけ必要な)場合は、 inner
join を使います。
●
左側のテーブルに対応するデータが右側のテーブルに
無いかもしれない場合で、しかもその場合でも左側の
データが必要な場合は left outer join を使います。
08/08/2014 8
「繋ぐためのテーブル」が分からない
●
ER 図を見てみましょう。
●
線をたどって行くと、取得したいデータがあるテーブ
ルと絞り込み条件になるデータがあるテーブルが繋が
るはずです。その間にあるテーブルが「繋ぐための
テーブル」です。
08/08/2014 9
取得したいデータが複数のテーブルにある
●
いいんです。みんな集めて、みんな繋ぎましょう。
●
絞り込み条件になるデータがあるテーブルが複数ある場
合も同じです。
●
時には 1 つのテーブルを別々の意味で 2 回必要とする
場合もあります。ここで別名( AS の後ろに書くや
つ)が活躍します。別の別名をつければ、別のテーブ
ルとして扱えます。
08/08/2014 10
取得したいデータがテーブルに無い
●
おそらく複数のデータを元に加工したデータが必要なの
でしょう。その場合は、加工に必要なデータが 1 行に収
まるようにテーブルを集めて繋いでみましょう。
●
あとで select 句で加工できます。
●
「 col_1 が A の時は col_2 の値を、 col_1 が B の時は
col_3 の値を表示する」等は、 case 式が活躍するパ
ターンです。
●
重要なのは「 1 行にまとめる」ことです。
08/08/2014 11
取得したいデータがテーブルに無い
●
1 つの列の値の合計、最小、最大等集合関数が必要な場
合は、 group by の出番です。
●
特殊なものとしては partition by もあります。
– select 句に使う partition by は、テーブル定義の partition とはちょっ
と違います。
●
Group by した結果を 1 つのテーブルとして扱って、他
のテーブルと繋ぐ場合もあります。
– サブクエリとか inline view と呼ばれます。
08/08/2014 12
勝負は 1 行に集められるかどうかです。
●
取得したいデータやそれを作り出すために必要なデー
タ、それらを絞り込むために必要なデータ、それらを全
て 1 行に集められれば SQL は書けたも同然です。
08/08/2014 13
その接続条件で大丈夫ですか?
●
片方のテーブルの 1 つの行に対し、もう片方のテーブル
において接続条件が true となる行が複数存在する場合、
全体として行数が増える場合があります。
●
その条件で一意に行が特定できますか?
– テーブル定義書を見て確認しましょう。
●
それは意図的ですか?
– トランザクションデータ 1 に対し、マスタデータ n ならば、たぶん
間違いです。
– N:N になる場合は、マスタだろうとトランザクションだろうと、ま
ず間違いなく間違いです。
08/08/2014 14
次に絞り込む
●
必要なデータは集めてあるはずです。等式、不等
式、 between や and 、 or を使ってガンガン絞り込みま
しょう。
●
in は「 or の特殊なパターン」だと考えておくと良い
と思います。
– in にサブクエリを書いて exists の代わりにすることも出来るのです
が、まず使いません。
08/08/2014 15
次に絞り込む
●
実は、等式、不等式、 in 、 exists などは全て「述語」で
す。
●
述語は、 true 、 false 、 unknown のいずれかを返す
「関数的なもの」です。
– true と false の二値だけではないので、三値論理と呼ばれます。
●
where 句は、述語を論理演算子( and や or )で繋ぐこと
で出来ています。そして、 where 句全体が true になる行
だけを残して絞り込みます。
●
順番はありません。「 true になるか、ならないか」
それだけです。
08/08/2014 16
join か exists か
●
Inner join と exists 、 left outer join + is null と not exists
は(ほぼ)同じことができますが、特殊な条件が揃わな
い限り exists の方が安全です。
●
exists 述語のサブクエリ部分は、他の述語で絞り込ま
れた結果の全ての行に対して 1 行ごとに 1 回実行され
ます。このため、このサブクエリが遅いと select 文全
体がとても遅くなります。また、 exists 述語に渡され
る中間結果の行数が極端に多い場合も遅くなります。
●
Inner join や left outer join は行が増えることがありま
す。増やしたくない場合は exists 述語を使用するか、
もっと特殊なことをする必要があります。
08/08/2014 17
その絞り込み条件で大丈夫ですか?
●
Left outer join で接続した右側のテーブルの列が where 句
に登場したら、それはたぶん間違っています。
●
Left outer join で接続したということは、接続後のその
列には値の無い( null )行が存在するということで
す。
– それ、絞り込んで良いですか?意図的ですか?
08/08/2014 18
その絞り込み条件で大丈夫ですか?
●
「試しに実行したら問題が無かった。」
●
テストデータに不足はないですか?
●
本当にその条件だけで不必要なデータは残りません
か?
– テーブル定義書を確認してみましょう。
08/08/2014 19
そして加工する
●
必要なデータは全て集まっているはずです。
●
不必要な行も既に絞り込まれています。
●
あとは必要な列を選択するだけです。
●
計算や文字列接続、 case 式、 coalesce 等の関数を駆
使して欲しい情報を作り出すこともあります。
●
ユーザー定義関数( stored function )の主戦場もココ
です。
08/08/2014 20
最後に表示する
●
SQL は集合演算です。なので「順序」という概念があり
ません。しかし表示する際には、特定の順序で表示され
て欲しかったり行番号が欲しかったりします。そのため
にあるのが、 order by や row_number 関数です。
08/08/2014 21
サブクエリはいつ使うのか
●
一番多いのは集合関数の結果を絞り込み条件に使いたい
場合です。
●
この場合は having 句でも実現できます。
●
Left outer join の右側のテーブルを絞り込んでおきたい場
合にも、よく使用します。
●
上記の 2 つ意外だと、 exists 述語くらいでしょう
か。。。
08/08/2014 22
まとめ
●
SQL は、集めて繋いで、絞って、加工して、表示する。
●
「集めて繋ぐ」行程が勝負のカギ。
●
ここで必要な情報を 1 行にまとめられるかどうかで勝
負が決まる。
08/08/2014 23
登場しなかったもの – union
●
完全に別々の SQL から 1 つの結果を作り出すときに使い
ます。
●
テーブル設計が特殊なとき以外、あまり登場しませ
ん。
●
通常は、 or か left outer join + coalesce で実現できま
す。
●
登場する場合は、 union all (重複を削除しない
union )として使用します。
– その方が速いので。
08/08/2014 24
登場しなかったもの – distinct
●
検索結果から重複したものを取り除きます。
●
Group by に select 句に登場する全ての列を並
べ、 select 句で集合関数を使わないことでも代用でき
ます。
●
テーブル設計がおかしいか、 select 文がおかしいか、
どちらかを誤摩化していることが多いです。
08/08/2014 25
登場しなかったもの – right outer join
●
Left outer join の左右が反対のやつです。
●
左右を入れ替えれば left outer join になりますので、左
右を入れ替えてしまいましょう。
– 混在すると分かり難くなるので。
●
ちなみに left join 、 right join は、それぞれ left outer
join 、 right outer join の省略形です。
08/08/2014 26
登場しなかったもの – full outer join
●
Left outer join の場合、右側のテーブルの行は左側のテー
ブルに対応するものが無ければ絞り込まれます。しか
し、そんなときにも行を残すのが full outer join です。
●
左右のテーブルの全ての行が結果として取得される
(絞り込んでいる場合は別)ということです。
●
あまり欲しいと思ったことはありません。
– 過去に 1 回だけ使ったことがある気がする。けど、忘れた。
08/08/2014 27
登場しなかったもの – cross join
●
左右のテーブルの全ての行の全ての組み合わせが結果と
して取得されます。
●
みんな大好きデカルト積
●
結合条件はありません。
●
from 句にテーブル名をカンマ区切りで並べた状態と同じ
です。
●
この書き方をした上、絞り込み条件が不足すること
で、意図せず cross join になっているバグをよくみま
す。
– なので意図的に使う時は cross join と明示します。

SQLを書こう