備忘録的プログラミングリファレンス

外部結合 OUTER JOIN

 テーブルの結合には内部結合と外部結合があります。

 テーブルの結合においてどちらか一方のデータをすべて網羅したいときには外部結合( OUTER JOIN )を使用します。

内部結合
SELECT * FROM goods JOIN sales_slip ;

 上記のようにJOINのみでは内部結合として処理されます。外部結合の場合はOUTER JOINと明記する必要があります。

外部結合
SELECT * FROM goods AS g
	LEFT OUTER JOIN sales_slip AS s
	ON g.id = s.goods_id;

 さらにOUTER JOINの前にLEFTまたはRIGHTといった先のテーブルの内容を優先するのか後のテーブルのデータを優先するのかと、ONによる結合条件を設定しないとエラーになります。

 内部結合と外部結合の違いは、内部結合は片方のテーブルデータがない組み合わせは除外されます。 外部結合は、LEFTまたはRIGHTで一方のテーブルデータを網羅するようにします。

 例えば以下の例では商品名マスタと売上を外部結合しています。LEFTによって商品マスタのすべてのデータを網羅します。売上に商品名がない場合は NULL で表現されます。

外部結合
> SELECT * FROM goods g LEFT OUTER JOIN sales_slip s ON g.id=s.goods_id;
+----+----------------+------------+------+---------------------+----------+------------+--------+---------+
| id | name           | unit_price | id   | date_time           | goods_id | unit_price | amount | user_id |
+----+----------------+------------+------+---------------------+----------+------------+--------+---------+
|  1 | candy          |          3 |    4 | 2023-09-28 22:42:10 |        1 |          5 |     80 |       2 |
|  1 | candy          |          3 |    1 | 2023-10-29 07:17:00 |        1 |          3 |    100 |       1 |
|  2 | snack          |          5 |    3 | 2023-09-28 22:42:10 |        2 |          5 |     30 |       1 |
|  2 | snack          |          5 |    2 | 2023-09-26 22:29:58 |        2 |          8 |     50 |       1 |
|  3 | chocolate      |          8 |    5 | 2023-09-28 22:42:53 |        3 |         10 |     10 |       3 |
|  4 | ice            |          8 | NULL | NULL                |     NULL |       NULL |   NULL |    NULL |
|  5 | lollipop candy |         15 | NULL | NULL                |     NULL |       NULL |   NULL |    NULL |
+----+----------------+------------+------+---------------------+----------+------------+--------+---------+

 優先された商品マスタのデータはすべて網羅する形になり、売れていない商品が分かりやすくなります。

例のデータについて

 以降の例では、テーブルページの外部キー制約( FOREIGN KEY )の作成例で使用したデータを使用しています。

 以下のようなデータです。

SELECT * FROM goods;
+----+----------------+------------+
| id | name           | unit_price |
+----+----------------+------------+
|  1 | candy          |          3 |
|  2 | snack          |          5 |
|  3 | chocolate      |          8 |
|  4 | ice            |          8 |
|  5 | lollipop candy |         15 |
+----+----------------+------------+
...

select * from sales_slip;
+----+---------------------+----------+------------+--------+
| id | date_time           | goods_id | unit_price | amount |
+----+---------------------+----------+------------+--------+
|  1 | 2023-09-26 22:29:57 |        1 |          3 |    100 |
|  2 | 2023-09-26 22:29:58 |        2 |          8 |     50 |
|  3 | 2023-09-28 22:42:10 |        2 |          5 |     30 |
|  4 | 2023-09-28 22:42:10 |        1 |          5 |     80 |
|  5 | 2023-09-28 22:42:53 |        3 |         10 |     10 |
+----+---------------------+----------+------------+--------+
...

OUTER JOIN

 外部結合ではOUTER JOIN句を使用します。さらにLEFTまたはRIGHTと組み合わせ条件であるON句も必要になります。

 LEFTまたはRIGHTはすべてのデータを網羅したい側のテーブルを指定します

 例えば、以下のように商品マスタと売上データを結合しているのですが、商品マスタのデータをすべて網羅しています。
 このことで各商品の販売数を知ることができ、また売れていない商品を知ることができます。

外部結合
> SELECT * FROM goods g
	LEFT OUTER JOIN sales_slip s
	ON g.id=s.goods_id;
+----+----------------+------------+------+---------------------+----------+------------+--------+---------+
| id | name           | unit_price | id   | date_time           | goods_id | unit_price | amount | user_id |
+----+----------------+------------+------+---------------------+----------+------------+--------+---------+
|  1 | candy          |          3 |    4 | 2023-09-28 22:42:10 |        1 |          5 |     80 |       2 |
|  1 | candy          |          3 |    1 | 2023-10-29 07:17:00 |        1 |          3 |    100 |       1 |
|  2 | snack          |          5 |    3 | 2023-09-28 22:42:10 |        2 |          5 |     30 |       1 |
|  2 | snack          |          5 |    2 | 2023-09-26 22:29:58 |        2 |          8 |     50 |       1 |
|  3 | chocolate      |          8 |    5 | 2023-09-28 22:42:53 |        3 |         10 |     10 |       3 |
|  4 | ice            |          8 | NULL | NULL                |     NULL |       NULL |   NULL |    NULL |
|  5 | lollipop candy |         15 | NULL | NULL                |     NULL |       NULL |   NULL |    NULL |
+----+----------------+------------+------+---------------------+----------+------------+--------+---------+

 amount が販売数ですが、NULL で表示された商品は販売データがないことを示しています。
 この結果を GROUP BY で商品ごとにグループ化すれば商品ごとの販売数を知ることができます。

 外部結合では必ずOUTER JOIN句を使用します。JOINだけでは内部結合を示し、すべての組み合わせを網羅します。

 内部結合では片方のテーブルデータがない場合はその行は除外されます。
 例えば上記の外部結合を内部結合に換えると以下のような結果になります。

内部結合
SELECT * FROM goods AS g JOIN sales_slip AS s ON g.id = s.goods_id;
+----+-----------+------------+----+---------------------+----------+------------+--------+---------+
| id | name      | unit_price | id | date_time           | goods_id | unit_price | amount | user_id |
+----+-----------+------------+----+---------------------+----------+------------+--------+---------+
|  1 | candy     |          3 |  1 | 2023-10-29 07:17:00 |        1 |          3 |    100 |       1 |
|  2 | snack     |          5 |  2 | 2023-09-26 22:29:58 |        2 |          8 |     50 |       1 |
|  2 | snack     |          5 |  3 | 2023-09-28 22:42:10 |        2 |          5 |     30 |       1 |
|  1 | candy     |          3 |  4 | 2023-09-28 22:42:10 |        1 |          5 |     80 |       2 |
|  3 | chocolate |          8 |  5 | 2023-09-28 22:42:53 |        3 |         10 |     10 |       3 |
+----+-----------+------------+----+---------------------+----------+------------+--------+---------+

 内部結合では商品マスタにデータがあっても売上にないデータは除外されています。内部結合では、icelollipop candyが網羅されていません。

 内部結合についてはテーブルの結合 JOINページを参照してください。

RIGHT OUTER JOIN

 上記のLEFT OUTER JOINはこの句の左側のテーブルのデータをすべて網羅します。
 右側を中心に外部結合をすることもでき、その場合はRIGHT OUTER JOINとします。

外部結合 RIGHT OUTER JOIN
> SELECT * FROM goods g
	RIGHT OUTER JOIN sales_slip s
	ON g.id=s.goods_id;
+------+-----------+------------+----+---------------------+----------+------------+--------+---------+
| id   | name      | unit_price | id | date_time           | goods_id | unit_price | amount | user_id |
+------+-----------+------------+----+---------------------+----------+------------+--------+---------+
|    1 | candy     |          3 |  1 | 2023-10-29 07:17:00 |        1 |          3 |    100 |       1 |
|    2 | snack     |          5 |  2 | 2023-09-26 22:29:58 |        2 |          8 |     50 |       1 |
|    2 | snack     |          5 |  3 | 2023-09-28 22:42:10 |        2 |          5 |     30 |       1 |
|    1 | candy     |          3 |  4 | 2023-09-28 22:42:10 |        1 |          5 |     80 |       2 |
|    3 | chocolate |          8 |  5 | 2023-09-28 22:42:53 |        3 |         10 |     10 |       3 |
+------+-----------+------------+----+---------------------+----------+------------+--------+---------+

 上記の例でのRIGHT OUTER JOINでは売上データをすべて網羅します。売上データにあって商品マスタにはないデータがないため、ただ売上データを閲覧する形になっています。

ON 結合条件

 外部結合 OUTER JOIN では ON 句が必要になります。ON 句はテーブルの結合条件を付加するためのものです。

SELECT * FROM goods AS g LEFT OUTER JOIN sales_slip s
	ON g.id = s.goods_id;
+----+----------------+------------+------+---------------------+----------+------------+--------+---------+
| id | name           | unit_price | id   | date_time           | goods_id | unit_price | amount | user_id |
+----+----------------+------------+------+---------------------+----------+------------+--------+---------+
|  1 | candy          |          3 |    4 | 2023-09-28 22:42:10 |        1 |          5 |     80 |       2 |
|  1 | candy          |          3 |    1 | 2023-10-29 07:17:00 |        1 |          3 |    100 |       1 |
|  2 | snack          |          5 |    3 | 2023-09-28 22:42:10 |        2 |          5 |     30 |       1 |
|  2 | snack          |          5 |    2 | 2023-09-26 22:29:58 |        2 |          8 |     50 |       1 |
|  3 | chocolate      |          8 |    5 | 2023-09-28 22:42:53 |        3 |         10 |     10 |       3 |
|  4 | ice            |          8 | NULL | NULL                |     NULL |       NULL |   NULL |    NULL |
|  5 | lollipop candy |         15 | NULL | NULL                |     NULL |       NULL |   NULL |    NULL |
+----+----------------+------------+------+---------------------+----------+------------+--------+---------+

非等結合

 ON 句における条件でイコール=で評価される場合は等結合といいます。
 範囲による条件も指定することができます。範囲による結合は非等結合といいます。

 以下の例は非等結合ができることを示しているだけで意味がありません。

SELECT * FROM goods AS g LEFT OUTER JOIN sales_slip AS s
	ON g.id >= s.goods_id;

ON 句と WHERE 句

 テーブル結合に WHERE 句による抽出を加えるとその条件による行データのみが取得できます。

SELECT * FROM goods AS g LEFT OUTER JOIN sales_slip AS s
	ON g.id = s.goods_id
	WHERE amount >= 50;

+----+-------+------------+----+---------------------+----------+------------+--------+---------+
| id | name  | unit_price | id | date_time           | goods_id | unit_price | amount | user_id |
+----+-------+------------+----+---------------------+----------+------------+--------+---------+
|  1 | candy |          3 |  1 | 2023-10-05 20:46:53 |        1 |          3 |    100 |       1 |
|  2 | snack |          5 |  2 | 2023-09-26 22:29:58 |        2 |          8 |     50 |       1 |
|  1 | candy |          3 |  4 | 2023-09-28 22:42:10 |        1 |          5 |     80 |       2 |
+----+-------+------------+----+---------------------+----------+------------+--------+---------+

 このことは ON 句で AND を使用しても同じことが起きます。

> SELECT * FROM goods AS g LEFT OUTER JOIN sales_slip AS s
	ON g.id = s.goods_id
	AND amount >= 50;

+----+-------+------------+----+---------------------+----------+------------+--------+---------+
| id | name  | unit_price | id | date_time           | goods_id | unit_price | amount | user_id |
+----+-------+------------+----+---------------------+----------+------------+--------+---------+
|  1 | candy |          3 |  1 | 2023-10-05 20:46:53 |        1 |          3 |    100 |       1 |
|  2 | snack |          5 |  2 | 2023-09-26 22:29:58 |        2 |          8 |     50 |       1 |
|  1 | candy |          3 |  4 | 2023-09-28 22:42:10 |        1 |          5 |     80 |       2 |
+----+-------+------------+----+---------------------+----------+------------+--------+---------+

 ただし、処理の順番に違いがあります。同じ結果が得られるのですがコードの意味が目的とした手段に準じているかが大事です。