【SQL入門】最適なテーブル結合について

>この記事では、《テーブル結合》について、
業務を通して学習した内容を、備忘録としてまとめています。

- __テーブル結合のロジック__
- __最適な結合方法__

こういった内容についてまとめています。

__※本記事は、自分で学習したことのまとめ用として書いています。
尚、解説で誤った点があれば、スローして頂ければ喜んでキャッチしますのでお願い致します。__

## テーブル結合のロジック

![shells_script_3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255884/e995b8e9-c126-36fb-4f50-1b06add09e3c.jpeg)

まずは・・・

__"どう言う流れでテーブルが結合がされるか"__

を理解する必要があります。

そして、流れは下記の通りです。

1. &ensp;__`ON`句の条件で、テーブルを結合する__
2. &ensp;__`WHERE句`の条件で、抽出する__

### ---&ensp;具体例（内部結合）&ensp;---

__『内部結合』__は・・・

それぞれのテーブルで、`ON`句で指定したカラムの値が一致するものだけを結合します。

>『内部結合』_の命令には、`INNER JOIN`を使います。

#### person テーブル

| id | name | job_id |
| :---: | :---: | :---: |
| 1 | A | 1 |
| 2 | B | 2 |
| 3 | C | 3 |

#### job テーブル

| id | job_name |
| :---: | :---: |
| 1 | engineer |
| 2 | designer |

<br>

では・・・

上記２つのテーブルを例に、『テーブル結合のロジック』について見ていきましょう。

#### パターン①：&ensp;`ON`句のみ

~~~sql
SELECT
    id,
    person.name,
    job.job_name
FROM person
    INNER JOIN job
        ON person.job_id = job.id

-- job.idはjobのPK
~~~

##### 結果

| id | name | job |
| :---: | :---: | :---: |
| 1 | A | engineer |
| 2 | B | designer |

`ON`句で指定した`person.job_id = job.id`という条件で、テーブル結合しています。

また・・・

job テーブルに `id = 3`のレコードがないため、`C`のレコードは削除されています。

#### パターン②：&ensp;`ON`句と`WHERE句`

~~~sql
SELECT
    id,
    person.name,
    job.job_name
FROM person
    INNER JOIN job
        ON person.job_id = job.id
WHERE 
    job.id = '1';

-- job.idはjobのPK
~~~

##### 結果

| id | name | job |
| :---: | :---: | :---: |
| 1 | A | engineer |

パターン①の条件に加え、`WHERE句`で`job.id = '1'`を指定しているので…

`B`のレコードが削除されています。


## `LEFT OUTER JOIN`で気をつけること

![shells_script_1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255884/a5ffec4c-aca7-d012-5a58-d5669f5dffa2.jpeg)


### ---&ensp;処理の概要&ensp;---

`LEFT OUTER JOIN`では・・・

（`LEFT OUTER JOIN`の）左側のテーブルの行を全て抽出します。

そのため・・・

右側のテーブルに存在しない行については、`NULL`が埋められています。

ちなみに・・・

先ほどのコードを、`LEFT OUTER JOIN`で書き換えると…

~~~sql
SELECT
    id,
    person.name AS name,
    job.job_name AS job
FROM person -- ←左側
    LEFT OUTER JOIN job　-- ←右側
        ON person.job_id = job.id

-- job.idはjobのPK
~~~

となります。

その結果・・・

例えば、下記のようなデータが取れる可能性もあります。

| id | name | job |
| :---: | :---: | :---: |
| 1 | A | engineer |
| 2 | B | NULL |
| 3 | C | NULL |

繰り返しになりますが、結合処理のロジックは…

- __`ON`句の条件で、テーブルを結合する__

であるので、この後に、左側のテーブルの行を全て呼び出します。

その結果・・・

__"&ensp;取得する必要ない行に`NULL`が埋められて、呼び出される&ensp;"__

という訳です。

### ---&ensp;NULL対策&ensp;---

#### `WHERE句`の条件で、抽出する

~~~sql
SELECT
    id,
    person.name AS name,
    job.job_name AS job
FROM person
    LEFT OUTER JOIN job
        ON person.job_id = job.id
WHERE 
    job.id = '1';

-- job.idはjobのPK
~~~

上記のように・・・

`WHERE句`の条件で、抽出することで`NULL`を回避することができます。

| id | name | job |
| :---: | :---: | :---: |
| 1 | A | engineer |


## 最適な結合方法

![career-3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255884/a10baba0-6a83-3071-5513-bb061e1f3ac9.jpeg)


しかし、先ほどのコードだと・・・

仮に…

__"&ensp;100万件を超える大量のデータを持つテーブル同士を結合させる&ensp;"__

といった場合、処理が遅くなってしまいます。

>100万件のデータを結合した後に、`WHERE句`の条件で抽出するためです。

なので、大量のデータの場合は…

__"&ensp;結合する前に、必要なデータのみを抽出しておく&ensp;"__

のがオススメです。

~~~sql
WITH variation AS(
    SELECT *
    FROM
        person
    WHERE
        person.job_id = '1'
)

SELECT
    id,
    person.name AS name,
    job.job_name AS job
FROM variation
    LEFT OUTER JOIN job
        ON person.job_id = job.id
~~~

上記のSQLでは・・・

1. &ensp;__サブクエリの処理__<br>
➡︎&ensp;この時点で、`person.job_id = '1'`を抽出する
2. &ensp;__`ON`句で結合__

といった処理の流れとなります。

>__"&ensp;サブクエリを使って、あらかじめ必要なデータのみを抽出して、結合する件数を減らす。&ensp;"__
ということをしています。

これにより・・・

無駄なテーブル結合を削減できるうえ、左側（person）のテーブルはサブクエリによって抽出済みのため、

仮に、`LEFT OUTER JOIN`によって全行呼び出されたとしても…

最終的に取得できる結果は、`person.job_id = '1'のレコードのみになります。

| id | name | job |
| :---: | :---: | :---: |
| 1 | A | engineer |