SQLの複数テーブルの結合をおさらいしてみた(1)

#RDB（リレーショナルデータベース）
複数テーブルを結合する方法を紹介する前に、なぜ複数テーブルを用意するのかについて説明します。
まずテーブルを分割しなかった場合のテーブルを見てみましょう
![スクリーンショット 2021-10-26 22.05.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1265227/dce7ae59-804b-c25c-1145-43a8bb485cbd.png)


これを顧客名を顧客ID（外部ー）としてテーブルを２つに分割します。
![スクリーンショット 2021-10-26 22.35.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1265227/72f1bca7-478a-2b5a-80d1-dd803ad5c669.png)


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1265227/19d0aaa3-13e0-1bb9-14f9-f7b8049b4dae.png" width=70%>

（外部キー：上記の「顧客ID」列のように関連したテーブル間を結ぶために設定する列のこと）

私たちにとっては顧客IDのような数字で書かれるより、元の顧客名のほうが見やすいです。しかし、テーブルが分割されていたほうがデータを安全、確実、高速に取り扱うことができます。
仮に、いまテーブル分割がされていな元のテーブルで顧客名のディオをDIOに変えたいとします。（JOJOがわからない人はごめんなさい）

```sql:sql
UPDATE 注文
SET 顧客名 = 'ディオ'
WHERE 顧客名 = 'DIO'
```

現在は４件ですが注文テーブルに１０万件ほどデータが格納されていたとしましょう。このとき上記のコードではDBMSは一行ずつ条件に合致するか調べて書き換えます（合計１０万行）。しかし、切り分けた顧客テーブル（登場人物）は１０万人もいないので単純に調べる件数が少なく処理にかかる時間が少なくなります。

RDB（リレーショナルデータベース）とはこのように複数テーブルで分けるメリットを生かしたDBMSであると言えます。テーブルを複数分割して用意するメリットがわかったところで、RDBのテーブル結合についてまとめていきます。

#テーブルの結合
###テーブルの基本的な結合の仕方

```sql:sql
SELECT 選択列リスト
FROM テーブルA
JOIN　テーブルB
ON　両テーブルの結合条件
```

例えば最初に紹介したテーブルを結合してみましょう。今注文テーブル（分割ver）と顧客テーブルを結合します。


```sql:sql
SELECT 注文ID,注文日,顧客名,性別,個数
FROM 注文(分割ver)
JOIN　顧客
ON　注文(分割ver).顧客ID = 顧客.ID
```

ここで注意が必要なのが一行目に指定している列名の中に注文テーブル(分割ver)には存在していな顧客名,性別が使われている点です。通常存在しない列名をSLECT分の選択列リストに記入するとエラーになりますが３行目のJOINがあることで結合するテーブルの列名も参照可能になります。

###ON句での結合条件について
先程のコードでは

```sql:sql
ON　注文(分割ver).顧客ID = 顧客.ID
```

で結合条件を指定しています。これは
１．　注文(分割ver)の各行について顧客ID列のデータに注目する。
２．　顧客テーブルからそれと等しいIDを持つ行を取り出してつなぐ
といった条件指定になります。イメージとしては下記のイラストになります。

![スクリーンショット 2021-10-26 23.47.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1265227/8a99342e-de09-c436-c4be-0782fefe5fe4.png)

DBMSは注文(分割ver)を１行ずつ処理しながら「この行につなぐべき顧客IDはどれか」と探しながら行と行を結合しています。

それでは大まかな結合の概念がわかったところで具体的な特殊ケースでのDBMSの動き方をくわしく見ていきましょう。

##結合相手が複数行の場合
今までの例では結合結果が常に１行になりました。その理由としては右辺に指定している列（顧客テーブルのID列）は主キーでありユニークな値であったからです。しかし右辺に重複する値を含む列を指定した場合はどうなるでしょうか。例えば以下のテーブルを用意します。
![スクリーンショット 2021-10-27 0.10.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1265227/afa59bbb-7045-a730-a4ef-b20ce351a572.png)

１つの行に２つの行をつなぐことはできません。このようなケースは左表の行数が足りないので足りるまで足されます。つまり今回のケースでは
![スクリーンショット 2021-10-27 0.19.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1265227/830c7cc0-f924-8bc6-6319-eee28d440890.png)

このように右表の行数に合わせて左表の行を複製して結合します。したがって、左表に対して重複がある列を対象とした結合を行うと結合前より行数が増えます。

##結合相手の行がない場合
以下のテーブルを用意します。
![スクリーンショット 2021-10-27 0.26.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1265227/f1655c3d-d038-2aa5-b943-8c69db9c9cbc.png)

右表にはID４が存在しません。結合の際に次のようなクエリを実行したとしても結果は常に０行になります。

```sql:sql
SELECT　　* FROM 費目 WHERE 4 = 費目.ID
```

このようなケースではDBMSはこの行の結合を諦め次の行の処理に進みます。したがってもともと左表（家計簿）にあった費目ID４の行は実行結果から消滅してしまいます。

![スクリーンショット 2021-10-27 0.32.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1265227/b3bac123-24df-8d42-2ad6-accdc4b852ee.png)

では、このようなクエリを実行した場合はどうなるでしょうか。

```sql:sql
SELECT　　* FROM 費目 WHERE NULL = 費目.ID
```

この場合、NULLの箇所が結合されそうに思えますが実行結果から消滅してしまいます。その理由としてはNULLはどのような値と比較しても等しくならない特別な存在だからです。

###次回に続く、、。

##参考
書籍： スッキリわかるSQL入門 第2版 ドリル222問付き！　
　　　 中山 清喬　著/飯田 理恵子　著