INNER JOIN と OUTER JOIN【初心者から中級者へ】

SQLではデータを取得するときに自分が望む形で取得できない場合がしばしばあります。
``JOIN``を使えば複数のテーブルを結合してほしい形でのデータ取得が可能になります。
初心者のうちは「INNER JOIN」と「OUTER JOIN」の違いが分からず混乱します。
私もそうでした…

この記事では、**INNER JOIN と OUTER JOIN の違い**を解説しつつ、実務での使い方や注意点も紹介します。
脱初心者のためにJOINについて理解を深めましょう。

# JOINとは？
``JOIN``は取得したいデータに関連する複数のテーブルからデータを整形して取得するために使用します。
JOINには大きく分けて2種類あります。
* **INNER JOIN**
* **OUTER JOIN**
  * LEFT OUTER JOIN
  * RIGHT OUTER JOIN
  * FULL OUTER JOIN

# INNER JOIN：共通するデータだけを取得する
INNER JOINは、**両方のテーブルで条件に合致するデータだけ**を取得します。
まずは基本構文を確認しましょう。
```sql:基本構文
SELECT column1, column2  
FROM table1  
INNER JOIN table2  
ON table1.common_column = table2.common_column;
```

具体例を用いて考えましょう。
以下2つのテーブルを考えます。
**employees（社員テーブル）**
| employee_id | name | department_id |
|:-----------|:------------|:------------|
| 1          | Tanaka      | 101         |
| 2          | Suzuki      | 102         |
| 3          |Yamada       |   NULL      |

**departments（部署テーブル）**
| department_id | department_name |  
|:-----------|:------------|
| 101          | Sales      | 
| 102          | HR     | 
| 103          |Marketing       | 

``INNERJOIN``を使ってみましょう。
```sql:INNER JOIN
SELECT employees.name, departments.department_name  
FROM employees  
INNER JOIN departments  
ON employees.department_id = departments.department_id;
```
結果は以下のようになります。
| name| department_name |  
|:-----------|:------------|
|     Tanaka      | Sales      | 
| Suzuki          | HR     | 

Yamadaは``department_id``が一致しないので出力されません。
共通しているものしか抽出されないのです。

# OUTER JOIN：不足しているデータも含めて取得する
OUTER JOINは条件に合わないデータも取得できるのが特徴です。
種類ごとに動作が異なります。
* LEFT OUTER JOIN
* RIGHT OUTER JOIN
* FULL OUTER JOIN

## LEFT OUTER JON：左のテーブルの全データ取得する
```sql:基本構文
SELECT column1, column2  
FROM table1  
LEFT OUTER JOIN table2  
ON table1.common_column = table2.common_column;
```
左側（``table1``)のデータをすべて取得し、条件に合わない場合は右側（``table2``）にNULLを挿入します。
具体例で確認しましょう。
```sql:基本構文
SELECT employees.name, departments.department_name  
FROM employees  
LEFT OUTER JOIN departments  
ON employees.department_id = departments.department_id;
```
このSQLに対する結果はこうなります。
| name| department_name |  
|:-----------|:------------|
|     Tanaka      | Sales      | 
| Suzuki          | HR     | 
| Yamada        | NULL     |

理解できましたか？
同じ要領で``RIGHT OUTER JOIN``を確認しましょう。
## RIGHT OUTER JOIN：右のテーブルの全データを取得
```sql:基本構文
SELECT column1, column2  
FROM table1  
RIGHT OUTER JOIN table2  
ON table1.common_column = table2.common_column;
```
``RIGHT OUTER JOIN``は右側（``table2``）のデータをすべて取得し、条件に合わない場合は左側（``table1``）にNULLを挿入します。

こちらも具体例で確認しましょう。
```sql:
SELECT employees.name, departments.department_name  
FROM employees  
RIGHT OUTER JOIN departments  
ON employees.department_id = departments.department_id;
```
結果はこちらになります。
| name| department_name |  
|:-----------|:------------|
|     Tanaka      | Sales      | 
| Suzuki          | HR     | 
| NULL        | Marketing     |

## FULL OUTER JOIN：両方のテーブルの全データを取得
```sql:基本構文
SELECT column1, column2  
FROM table1  
FULL OUTER JOIN table2  
ON table1.common_column = table2.common_column;
```
``FULL OUTER JOIN``では両方のテーブルのすべてのデータを取得します。
条件に合わない場合はNULLを挿入します。

では具体例を見てみましょう。
```sql:基本構文
SELECT 
    employees.employee_id, 
    employees.name AS employee_name, 
    employees.department_id AS emp_department_id, 
    departments.department_name 
FROM 
    employees 
FULL OUTER JOIN 
    departments 
ON 
    employees.department_id = departments.department_id;
```
結果は下のようになります。
| employee_id| employee_name| emp_department_id| department_name |  
|:-----------|:------------|:------------|:------------|
| 1          | Tanaka      | 101         |Sales        |
| 2          | Suzuki      |102          |HR           |
| 3          |Yamada       | NULL        | NULL        |
| NULL       |NULL         |103          |Marketing    |

## 実務で役立つJOINの考え方
* **INNER JOINはデフォルト**：条件に一致しないデータは基本的に不要であることが多いので、INNER JOINをデフォルトとして考えましょう。
* **OUTER JOINのOUTER省略は非推奨**：``RIGHT JOIN``や``LEFT JOIN``といった書き方も可能です。しかし、内部結合(``INNER JOIN``)に対して外部結合(``OUTER JOIN``)であることを明示したいので省略することはお勧めしません。
* **データ量に注意**：JOINを使うと結合後のデータ量が増加することがあるので、取得するカラムや条件を適切に設定しましょう。

以上です。