【SQL】JOINの種類や応用的な使い方について解説
SQLを用いてデータベースから情報を抽出する際、JOINを用いてテーブルを結合することがあります。本記事では、おもなJOINの種類や応用的な使い方について解説します。
SQLを用いてデータベースから情報を抽出する際、JOINを用いてテーブルを結合することがあります。本記事では、おもなJOINの種類や応用的な使い方について解説します。
スキルアップ
2022/12/26 UP
- プログラミング
- インフラエンジニア
- 開発
SQLを用いてデータベースから情報を抽出する際、JOINを用いてテーブルを結合することがあります。本記事では、おもなJOINの種類や応用的な使い方について解説します。RDBMS(リレーショナルデータベース管理システム)にてデータを分析するのに必要な操作となるので、参考にしてください。
JOINとは
JOINとは複数のテーブルの行を条件によって結合する操作のことで、結合したテーブルから任意の情報を取得可能です。リレーショナルデータベースにおいて、テーブルを分けて情報の重複をなくした状態で保存することを正規化と呼びます。正規化によってデータの冗長性をなくし、一貫性のある状態で保存できることが利点です。
正規化された複数のテーブルからデータの確認や分析を行なうとき、JOINを用いてデータを連結します。テーブルにおいて、ある1つのレコードを特定できるカラムはプライマリーキー、他のテーブルの情報を特定するカラムは外部キーと呼ばれています。2つのテーブルのプライマリーキーと外部キーをJOINで結びつけると、一致する情報を同じレコードとして抽出できます。
本記事では、下記の商品データ(productテーブル)と明細データ(receiptテーブル)を用いて解説します。各データは以下のような内容です。
商品データ(productテーブル)
product_cd (商品コード) |
category_cd (カテゴリコード) |
unit_cost (単価) |
---|---|---|
1001 | 2001 | 300 |
1002 | 2001 | 500 |
1003 | 2002 | 620 |
1004 | 2002 | 450 |
1005 | 2003 | 800 |
1006 | 2003 | 1020 |
明細データ(receiptテーブル)
receipt_cd (明細コード) |
sales_ymd (販売日) |
customer_id (顧客ID) |
product_cd (製品コード) |
quantity (数量) |
---|---|---|---|---|
3001 | 20190422 | CS906414 | 1001 | 10 |
3002 | 20190511 | CS908457 | 1003 | 2 |
3003 | 20190823 | CS928987 | 1005 | 1 |
3004 | 20191205 | CS900321 | 1007 | 7 |
3005 | 20200406 | CS900478 | 1003 | 5 |
3006 | 20201013 | CS903119 | 1001 | 12 |
3007 | 20210625 | CS909984 | 1002 | 9 |
JOIN の種類
おもなJOINの種類を5つ紹介します。選択するJOINによって2つのテーブルの結合の仕方が異なるため、違いを理解して使い分ける必要があります。
INNER JOIN:内部結合
INNER JOINは、指定した結合条件が両方のテーブルでマッチするレコードのみを返します。FROM内にてON句を用いて結合する条件を指定します。INNERは含めず、JOINのみ記載しても構いません。
下記のコードでは、productテーブルとreceiptテーブルを、product_cd(商品コード)が一致するという条件で内部結合しました。各テーブルはASを用いて別名(エイリアス名)を付けていることにご注意ください。SELECT内で出力するカラムを選択しています。
※サンプルコード
SELECT pr.*, re.receipt_cd, re.quantity FROM product AS pr INNER JOIN receipt AS re ON pr.product_cd = re.product_cd
行 | product_cd | category_cd | unit_cost | receipt_cd | quantity |
---|---|---|---|---|---|
1 | 1001 | 2001 | 300 | 3001 | 10 |
2 | 1001 | 2001 | 300 | 3006 | 12 |
3 | 1002 | 2001 | 500 | 3007 | 9 |
4 | 1003 | 2002 | 620 | 3002 | 2 |
5 | 1003 | 2002 | 620 | 3005 | 5 |
6 | 1005 | 2003 | 800 | 3003 | 1 |
INNER JOINを用いると、2つのテーブルで共通するproduct_cd(商品コード)である1001、1002、1003、1005の4つのレコードを抽出されました。
LEFT (OUTER) JOIN:左外部結合
LEFT JOINは、左テーブルのレコードすべてと、結合条件にマッチする右テーブルのレコードを返します。「左テーブル LEFT JOIN 右テーブル」のように記述し、OUTERは省略可能です。基準となるテーブルに、別のテーブルから情報を追加するという使い方ができます。右テーブルのレコードで結合条件にマッチしない場合はNULLが返されます。
以下のコードでは、productテーブルを左、receiptテーブルを右として、product_cd(商品コード)のデータをもとに左外部結合しました。
※サンプルコード
SELECT pr.*, re.receipt_cd, re.quantity FROM product AS pr LEFT JOIN receipt AS re ON pr.product_cd = re.product_cd
行 | product_cd | category_cd | unit_cost | receipt_cd | quantity |
---|---|---|---|---|---|
1 | 1001 | 2001 | 300 | 3001 | 10 |
2 | 1001 | 2001 | 300 | 3006 | 12 |
3 | 1002 | 2001 | 500 | 3007 | 9 |
4 | 1003 | 2002 | 620 | 3002 | 2 |
5 | 1003 | 2002 | 620 | 3005 | 5 |
6 | 1004 | 2002 | 450 | null | null |
7 | 1005 | 2003 | 800 | 3003 | 1 |
8 | 1006 | 2003 | 1020 | null | null |
左側のproductテーブルにはproduct_cd(商品コード)が1001~1006まで1レコードずつあり、すべて抽出されています。一方で右側のreceiptテーブルのレコードには1004や1006のレコードが含まれていません。receiptテーブルが持っているreceipt_cd(明細コード)やquantity(数量)のデータには、nullと表示されています。
RIGHT (OUTER) JOIN:右外部結合
RIGHT JOINとは、LEFT JOINの左右の関係を逆にしたものです。わかりづらくなるためLEFT JOINに統一することが多く、RIGHT JOINは使用頻度が低いといえるでしょう。「左テーブル RIGHT JOIN 右テーブル」のように記述したとき、右テーブルのレコードすべてと、結合条件にマッチする左テーブルのレコードを返します。
※サンプルコード
SELECT pr.*, re.receipt_cd, re.quantity FROM product AS pr RIGHT JOIN receipt AS re ON pr.product_cd = re.product_cd
行 | product_cd | category_cd | unit_cost | receipt_cd | quantity |
---|---|---|---|---|---|
1 | 1001 | 2001 | 300 | 3001 | 10 |
2 | 1001 | 2001 | 300 | 3006 | 12 |
3 | 1002 | 2001 | 500 | 3007 | 9 |
4 | 1003 | 2002 | 620 | 3002 | 2 |
5 | 1003 | 2002 | 620 | 3005 | 5 |
6 | 1005 | 2003 | 800 | 3003 | 1 |
7 | null | null | null | 3004 | 7 |
右側のreceiptテーブルに含まれるproduct_cd(商品コード)の値をもとに、右外部結合しています。receiptテーブルのproduct_cdに含まれる1001、1002、1003、1005のレコードが抽出されています。
7行目はreceiptテーブルにおけるproduct_cdが1007のレコードに該当しますが、抽出結果ではnullと表示されました。なぜならSELECT内では、「pr.*」のようにproductテーブルに含まれるproduct_cdを表示するよう指定していたので、productテーブルに含まれないデータは表示されないためです。
FULL (OUTER) JOIN:完全外部結合
左右のテーブルにおいて、指定した結合条件にマッチしないレコードも含めたすべてのレコードを抽出します。条件にマッチしなくても、テーブルが保有する全レコードの情報を表示して欲しい場合に使用するとよいでしょう。
※サンプルコード
SELECT pr.*, re.receipt_cd, re.quantity FROM product AS pr FULL JOIN receipt AS re ON pr.product_cd = re.product_cd
行 | product_cd | category_cd | unit_cost | receipt_cd | quantity |
---|---|---|---|---|---|
1 | 1004 | 2002 | 450 | null | null |
2 | 1003 | 2002 | 620 | 3002 | 2 |
3 | 1003 | 2002 | 620 | 3005 | 5 |
4 | 1005 | 2003 | 800 | 3003 | 1 |
5 | 1001 | 2001 | 300 | 3001 | 10 |
6 | 1001 | 2001 | 300 | 3006 | 12 |
7 | 1006 | 2003 | 1020 | null | null |
8 | 1002 | 2001 | 500 | 3007 | 9 |
9 | null | null | null | 3004 | 7 |
上記のコードの結果ですが、ON句で指定した条件とマッチしなかったレコードも表示されています。9行目がproductテーブルでマッチしなかったレコードで、1と7行目がreceiptテーブルでマッチしなかったレコードです。テーブル内に該当するレコードが見つからないときはnullと表記しています。
JOINの応用
JOINの基本的な使い方について解説しましたが、保存されているテーブルの関係によっては、これまでの方法では対応できないことがあります。JOINの応用的な使い方について紹介します。
複数の結合条件がある場合
結合条件はON句で指定しますが、結合条件が複数ある場合はAND演算子で条件式をつなげることができます。データの抽出条件を細かく指定したい場合や、テーブルの持つプライマリーキーが2カラム以上ある場合などに使用するとよいでしょう。ON句にはAND以外にもORやNOTなど任意の条件式が使えます。
以下のコードでは、INNER JOINでproductテーブルとreceiptテーブルを内部結合する際の条件を2つ指定しました。product_cd(商品コード)の一致と、category_cd(カテゴリコード)が2001という条件です。
※サンプルコード
SELECT‘ pr.*, re.receipt_cd, re.quantity FROM product AS pr INNER JOIN receipt AS re ON pr.product_cd = re.product_cd AND category_cd = 2001
行 | product_cd | category_cd | unit_cost | receipt_cd | quantity |
---|---|---|---|---|---|
1 | 1001 | 2001 | 300 | 3001 | 10 |
2 | 1001 | 2001 | 300 | 3006 | 12 |
3 | 1002 | 2001 | 500 | 3007 | 9 |
出力されたテーブルはINNER JOINによる内部結合した結果から、さらにcategory_cdが2001のレコードのみを抽出しています。
複数のテーブルを結合する場合
あるテーブルが外部キーを複数持つとき、欲しい情報が3つ以上のテーブルに分かれていることがあります。その場合は3つ以上のテーブルをすべて結合する必要がありますが、JOINを複数並べて結合できます。今回は3つ目のテーブルとして、商品のカテゴリ名を記したcategoryテーブルを用意しました。
category_cd (カテゴリコード) |
category_name (カテゴリ名) |
---|---|
2001 | meat |
2002 | vegetable |
2003 | fruit |
以下のコードでは、INNER JOINを2回用いてproductテーブルにreceiptテーブルとcategoryテーブルを結合しています。ON句による結合条件の指定も追加することを忘れないようにしてください。SELECTにて、categoryテーブル内にあるcategory_name(カテゴリ名)も表示するよう指定しています。
※サンプルコード
SELECT pr.*, re.receipt_cd, re.quantity, ca.category_name FROM product AS pr INNER JOIN receipt AS re ON pr.product_cd = re.product_cd INNER JOIN category AS ca ON pr.category_cd = ca.category_cd
行 | product_cd | category_cd | unit_cost | receipt_cd | quantity | category_name |
---|---|---|---|---|---|---|
1 | 1001 | 2001 | 300 | 3001 | 10 | meat |
2 | 1001 | 2001 | 300 | 3006 | 12 | meat |
3 | 1002 | 2001 | 500 | 3007 | 9 | meat |
4 | 1003 | 2002 | 620 | 3002 | 2 | vegetable |
5 | 1003 | 2002 | 620 | 3005 | 5 | vegetable |
6 | 1005 | 2003 | 800 | 3003 | 1 | fruit |
JOINの使い方のヒント
ここまでJOINの使い方について解説しましたが、合わせて覚えておくと役立つ方法や注意点を紹介します。
テーブル名の指定
JOINで複数のテーブルを結合するとき、同じ名称のカラムがあると、どちらのテーブルのものか判別ができません。カラム名を記載するときは、「テーブル名.カラム名」のようにテーブル名を明示する必要があります。可読性を考えると、カラム名が重複していなくても常にテーブル名も明示するとよいでしょう。
またテーブル名にはASを使ってエイリアス名を付けるのがおすすめです。「product AS pr」のように記載すると、productテーブルに「pr」というエイリアス名を付けられます。ほかの箇所では「pr」のみでテーブルを指定できるため、特にテーブル名が長い場合にはコードの記述量を大きく減らせます。
テーブル名を指定する際にデータベース名を合わせて指定する場合もあります。「データベース名.テーブル名」と記載する必要があり、毎回書くのは大変になるでしょう。その場合もエイリアス名を設定しておくと便利です。
USING
ここまではON句で結合条件を指定していましたが、USINGを用いると簡略的に表記できます。以下に示した例は、ON句で記載した結合条件についてUSINGを用いて記載しています。テーブル名を省略してカラム名のみ記載する形になるので、記述量を省略できます。
ON pr.product_cd = re.product_cd ↓ USING(product_cd)
複数の条件式を指定したい場合は、カラム名をカンマで区切って並べることで実行できます。下記の例では、product_cd1、product_cd2という2つの商品コード列を持つテーブルを結合したと想定しています。
ON pr.product_cd1 = re.product_cd1 AND pr.product_cd2 = product_cd2 ↓ USING(product_cd1, product_cd2)
ただし、USINGは利用するリレーショナルデータベース管理システムによっては利用できずエラーになる場合もあるのでご注意ください。
JOINで複数のテーブルから情報を抽出
リレーショナルデータベース管理システムで正規化されたテーブルから情報を抽出するとき、JOINを用いてテーブル同士を結合します。データの分析において、JOINは必須の操作となるでしょう。
内部結合や外部結合などさまざまな結合方法があるため、各結合の違いを理解して用途に応じて使い分けるようにしてください。複数の条件を指定してテーブルを結合したり、3つや4つなど複数のテーブルを結合したりも可能です。JOINを使いこなせるようになると、データベースから必要な情報のみ抽出できるため、効率的なデータ分析が可能となるでしょう。