SQLのJOINは、2つ以上のテーブルをつなげて、1回のSELECT文でまとめてデータを取り出すために用いられます。たとえば「顧客」と「住所」の情報が別々のテーブルに入っていても、JOINを使えば、顧客の名前と住所を1つの一覧として表示できます。
JOIN句とは
JOINで複数のテーブルを結びつける操作を「テーブル結合」と呼びます。JOINはSELECT文のFROM句に書き、結合する相手のテーブルと、どの列同士を対応させるか(結合条件)を指定して使います。構文の詳細は、MySQL公式リファレンスマニュアルのJOIN句で確認できます。SELECT文の基本は、以下の記事で解説しています。
データベースでは、同じ情報を何度も書かずに済むよう、データを複数のテーブルに分けて保存するのが一般的です。たとえば後述のサンプルでは、生徒の情報は「生徒」テーブルに、部活の競技名は「運動部」テーブルに分かれていて、「生徒」テーブルには部活IDだけが入っています。このように、別のテーブルの行を指し示すカラムを外部キーと呼び、JOINではこのカラムを「橋渡し」にして2つのテーブルを結びつけます。
テーブルを分けておくと、同じデータを何度も登録せずに済み、修正も1か所で済みます。その一方で、情報がテーブルごとにばらばらになるため、そのままでは「顧客の名前と住所を並べて見たい」といった要望に応えられません。JOINを使えば、必要なときだけテーブルをつなげて1つの表として取り出せるので、次のような場面で便利です。
- 顧客の一覧に、住所テーブルの住所や都市名を並べて表示する
- 注文データに、商品テーブルの商品名や価格を付けて一覧にする
- 顧客ごとの購入回数や支払い金額の合計のように、別のテーブルの情報を使って集計する
なお、外部キー制約(存在しない値を登録できないようにする設定)はデータの整合性を守るためのもので、テーブルを結合するのに必須ではありません。値が対応するカラムがあれば、制約がなくても結合できます。外部キー制約の仕様はMySQL公式リファレンスのFOREIGN KEYの制約のページで確認できます。外部キーについては、以下の記事で解説しています。
ここからはサンプルテーブルを用いてそれぞれの種類と適した用途を説明します。
サンプルテーブル
「JOIN」句の使い方の説明には、サンプルとして「生徒」テーブルと「運動部」テーブルを用います。
「生徒(student)」テーブル
| 学籍番号(number) | 氏名(name) | 部活ID(club_id) |
| 1 | 菊池 | 1 |
| 2 | 鈴木 | 3 |
| 3 | 田中 | 4 |
| 4 | 山田 | 1 |
「運動部(sport_club)」テーブル
| ID(id) | 競技(sport) |
| 1 | 野球 |
| 2 | サッカー |
| 3 | バスケットボール |
内部結合(INNER JOIN句)
「INNER JOIN」句は、それぞれのテーブルの指定したカラムで値が一致する場合にのみ、そのレコードを結合させるクエリです。この共通の値を持たないレコードは除外されます。ちなみに、単に「JOIN」と記述した場合も「INNER JOIN」句扱いになります。
図:内部結合(INNER JOIN句)のイメージ
結合条件は、等値結合 (=)のほかに、非等値結合(>、 <、 <>, !=、…)も使用可能です。
【使用イメージ】
SELECT カラム名 FROM テーブルA INNER JOIN テーブルB ON 結合条件;テーブルAから取得するカラムを指定し、結合の条件を満たすデータがテーブルBにある場合は1つのテーブルとして表示します。
【実行コード】
SELECT student.name,sport_club.sport FROM student INNER JOIN sport_club ON student.club_id = sport_club.id;「生徒」テーブルの「部活」カラムと「運動部」テーブルの「ID」カラムを紐づけ、結合条件を満たす場合に、「生徒」テーブルの「氏名」カラムと「運動部」テーブルの「競技」カラムを表示します。
【実行結果】
| 氏名 (name) | 競技 (sport) |
| 菊池 | 野球 |
| 鈴木 | バスケットボール |
| 山田 | 野球 |
共通する値がない「生徒」テーブルの田中さん、「運動部」テーブルの「サッカー」が表示されていません。
※下記コードでも同じ結果になります。
SELECT student.name,sport_club.sport
FROM student,sport_club WHERE student.club_id = sport_club.id;この書き方は、ONを使う構文が標準SQL(SQL92)で決められる前から使われてきた古い書き方で、現在もほとんどのデータベースで動きます。ただし、新しく書くときは結合条件をONに書くINNER JOINの形がおすすめです。理由は次の3つです。
- 結合条件(ON)と絞り込み条件(WHERE)が分かれるので、どの条件が何のためのものか読み取りやすい
- テーブルを1つ追加するたびにONを書くので、結合条件の書き忘れが起きにくい
- 標準の書き方なので、データベースの製品が変わってもそのまま使いやすい
また、MySQLでは「ON 結合条件」を書き忘れてもエラーにならず、2つのテーブルのすべての行の組み合わせ(4行×3行=12行)が返ります。これは後述する交差結合(CROSS JOIN)と同じ結果です。サンプルでは12行で済みますが、たとえば顧客599人と住所603件のテーブルなら、599×603=36万1,197行にもなります。結果の行数が想定より多いときは、ONの書き忘れを疑いましょう。
内部結合(INNER JOIN句)を使う場面
内部結合は、「部活に所属している生徒と、その競技名の一覧」のように、両方のテーブルにデータがそろっている行だけを取り出したいときに使います。結合する相手がいない行は結果から消えるため、全件を残したい場合は次に紹介する外部結合を使います。INNER JOINの使い方は、以下の記事でさらに詳しく解説しています。
左外部結合(LEFT OUTER JOIN句)
「OUTER JOIN」句は、値が一致するレコードに加えて、一致しない場合でも結合します。
基準にするテーブルによって次の3種類に分けられます。
- FROM句で指定したテーブル(左側)を基準にする「左外部結合(LEFT OUTER JOIN句)」
- JOIN句で指定したテーブル(右側)を基準にする「右外部結合(RIGHT OUTER JOIN句)」
- 両方のテーブルを基準にする「完全外部結合(FULL OUTER JOIN句)」※クエリ名は使用するRDBMSに依存
それぞれ説明していきます。
「LEFT OUTER JOIN」句は、結合条件を満たすレコードに加えて、結合条件に関わらず、テーブルAのレコードはすべて結合するクエリです。値が一致しない場合、テーブルBの該当箇所にはNULLが入ります。
図:左外部結合(LEFT OUTER JOIN句)のイメージ
【使用イメージ】
SELECT カラム名 FROM テーブルA LEFT OUTER JOIN テーブルB ON 結合条件;【実行コード】
SELECT student.name,sport_club.sport FROM student LEFT OUTER JOIN sport_club ON student.club_id = sport_club.id;カラム名の記述方法
カラム名は「テーブル名.カラム名」と記述すると可読性が上がるのでおすすめです。
エイリアス名の指定方法
「テーブル名 AS エイリアス名」と記述してテーブルに短い別名を付けると、全体の記述量を減らせます。たとえば「FROM student AS s INNER JOIN sport_club AS c ON s.club_id = c.id」と書けば、以降は「s.name」「c.sport」のように書けます(ASは省略可能)。
【実行結果】
| 氏名 (name) | 競技 (sport) |
| 菊池 | 野球 |
| 鈴木 | バスケットボール |
| 田中 | NULL |
| 山田 | 野球 |
INNER JOIN句では除外された田中さんが表示されています。これは、LEFT OUTER JOIN句の場合「生徒」テーブルのレコードをすべて出力するためです。「OUTER」は省略でき、「LEFT JOIN」と書いても同じ意味です。
【相手がいない行だけを取り出す】
NULLになった行に注目すると、「運動部に所属していない生徒」のような、相手のテーブルに対応するデータがない行だけを取り出せます。WHERE句で、右側のテーブルのカラムがNULLの行を指定します。
SELECT student.name FROM student LEFT OUTER JOIN sport_club ON student.club_id = sport_club.id WHERE sport_club.id IS NULL;実行結果は田中さんの1行だけになります。NULLかどうかは「= NULL」ではなく「IS NULL」で判定する点に注意しましょう(MySQL公式リファレンスのNULL値の操作のページ)。NULLの扱い方は、以下の記事で解説しています。
【ONとWHEREの違いに注意】
ONは「どの行同士を結びつけるか」、WHEREは「結合した結果からどの行を残すか」を指定します。外部結合では、右側のテーブルの条件をどちらに書くかで結果が変わります。
たとえば「野球部以外の競技を表示したい」として、条件をWHERE句に書くと次のようになります。
SELECT student.name,sport_club.sport FROM student LEFT OUTER JOIN sport_club ON student.club_id = sport_club.id WHERE sport_club.sport <> '野球';結果は鈴木さん(バスケットボール)の1行だけになり、田中さんも消えてしまいます。WHEREは結合後に評価されるため、sportがNULLの行も条件を満たさず除外されるからです。生徒を全員残したまま右側だけを絞り込みたいときは、条件をONに書きます。
SELECT student.name,sport_club.sport FROM student LEFT OUTER JOIN sport_club ON student.club_id = sport_club.id AND sport_club.sport <> '野球';こちらは生徒4人がすべて表示され、鈴木さんだけに「バスケットボール」、ほかの3人はNULLになります。WHERE句の使い方は、以下の記事で解説しています。
右外部結合(RIGHT OUTER JOIN句)
「RIGHT OUTER JOIN」句は、結合条件を満たすレコードに加えて、結合条件に関わらず、右側のテーブルのレコードはすべて結合するクエリです。値が一致しない場合、左側のテーブルの該当箇所にはNULLが入ります。
図:右外部結合(RIGHT OUTER JOIN句)のイメージ
【使用イメージ】
SELECT カラム名 FROM テーブルA RIGHT OUTER JOIN テーブルB ON 結合条件;【実行コード】
SELECT student.name,sport_club.sport FROM student RIGHT OUTER JOIN sport_club ON student.club_id = sport_club.id;【実行結果】
| 氏名 (name) | 競技 (sport) |
| 菊池 | 野球 |
| NULL | サッカー |
| 鈴木 | バスケットボール |
| 山田 | 野球 |
INNER JOIN句では除外されたサッカー部が表示されています。これは、RIGHT OUTER JOIN句の場合「運動部」テーブルのレコードをすべて出力するためです。
LEFTとRIGHTの違いは、どちらのテーブルの行をすべて残すかだけです。上のクエリは、テーブルの順番を入れ替えて「FROM sport_club LEFT OUTER JOIN student ON ~」と書いても同じ行が返ります(表示される列の順番は変わります)。FROM句に書いたテーブルが基準になるLEFT JOINのほうが読みやすいため、実務ではLEFT JOINに統一して書くことが多いです。
※ORDER BYを指定しない場合、結果の並び順は保証されません。実行環境によっては上の表と行の順番が異なることがあります。
完全外部結合(FULL OUTER JOIN句)
「FULL OUTER JOIN」句は、結合条件を満たすレコードに加えて、結合条件に関わらず、どちらか一方にしか存在しないデータも出力するクエリです。値が一致しない場合、相手側のテーブルの該当箇所にはNULLが入ります。2つのテーブルのデータを網羅的に結合できます。
FULL OUTER JOINはPostgreSQL・SQL Server・Oracleなどで使えますが、MySQLでは使えません(構文エラーになります)。MySQLで同じ結果を得たい場合は、後述の方法を使います。FULL OUTER JOINが使えるデータベースの書き方は、PostgreSQLのテーブル式(結合)のページやSQL Serverの結合のページで確認できます。
図:完全外部結合(FULL OUTER JOIN句)のイメージ
【使用イメージ】
SELECT カラム名 FROM テーブルA FULL OUTER JOIN テーブルB ON 結合条件;【実行コード】
SELECT student.name,sport_club.sport FROM student FULL OUTER JOIN sport_club ON student.club_id = sport_club.id;【実行結果】
| 氏名 (name) | 競技 (sport) |
| 菊池 | 野球 |
| NULL | サッカー |
| 鈴木 | バスケットボール |
| 田中 | NULL |
| 山田 | 野球 |
【MySQLで完全外部結合と同じ結果を得る方法】
LEFT OUTER JOINとRIGHT OUTER JOINの結果を、UNIONでつなげます。UNIONは重複する行を1つにまとめるため、両方に出てくる行が二重になりません。
SELECT student.name,sport_club.sport FROM student LEFT OUTER JOIN sport_club ON student.club_id = sport_club.id
UNION
SELECT student.name,sport_club.sport FROM student RIGHT OUTER JOIN sport_club ON student.club_id = sport_club.id;上の表と同じ5行(田中さんとサッカー部を含む)が返ります。UNIONの仕様はMySQL公式リファレンスのUNION句で確認できます。使い方は、以下の記事で解説しています。
外部結合(OUTER JOIN句)を使う場面
外部結合(OUTER JOIN句)は一致しないレコードも取得できるため、データ補完や欠損データの特定、関連するデータの統合などに使用されます。
よく使うのが、「部員が0人の部活も含めて、部活ごとの人数を数える」のような集計です。内部結合では部員のいないサッカー部が結果から消えてしまうため、運動部テーブルを基準にLEFT JOINします。このとき、COUNT(*)ではなくCOUNT(右側のテーブルのカラム)で数えるのがポイントです。
SELECT sport_club.sport, COUNT(student.number) AS 部員数
FROM sport_club LEFT OUTER JOIN student ON student.club_id = sport_club.id
GROUP BY sport_club.id, sport_club.sport;| sport | 部員数 |
| 野球 | 2 |
| サッカー | 0 |
| バスケットボール | 1 |
COUNT(*)は行の数を数えるため、相手がいなくて生徒の列がNULLになっているサッカー部の行も1と数えてしまいます。COUNT(カラム名)はNULL以外の値だけを数えるので、正しく0になります(MySQL公式リファレンスの集計関数の説明)。COUNT関数の使い方は、以下の記事で解説しています。
交差結合(CROSS JOIN句)
「CROSS JOIN」句は、2つのテーブルのすべての組み合わせを網羅的に生成するクエリで、交差結合は直積結合とも呼ばれます。結合条件(ON)は書きません。行数が「テーブルAの行数×テーブルBの行数」に膨らむため、使う場面は限られます。日付の一覧と店舗の一覧から「全日付×全店舗」の表を作るなど、すべての組み合わせが必要なときに使います。
【使用イメージ】
SELECT カラム名 FROM テーブルA CROSS JOIN テーブルB;【実行コード】
SELECT * FROM student CROSS JOIN sport_club;2つのテーブルのレコード分すべて掛け合わせているため、4 * 3で計12行になります。
【実行結果】
| 学籍番号 (number) | 氏名 (name) | 部活ID (club_id) | ID (id) | 競技 (sport) |
| 1 | 菊池 | 1 | 1 | 野球 |
| 1 | 菊池 | 1 | 2 | サッカー |
| 1 | 菊池 | 1 | 3 | バスケットボール |
| 2 | 鈴木 | 3 | 1 | 野球 |
| 2 | 鈴木 | 3 | 2 | サッカー |
| 2 | 鈴木 | 3 | 3 | バスケットボール |
| 3 | 田中 | 4 | 1 | 野球 |
| 3 | 田中 | 4 | 2 | サッカー |
| 3 | 田中 | 4 | 3 | バスケットボール |
| 4 | 山田 | 1 | 1 | 野球 |
| 4 | 山田 | 1 | 2 | サッカー |
| 4 | 山田 | 1 | 3 | バスケットボール |
※下記コードでも同じ結果になります。
SELECT *
FROM student,sport_club;3つ以上のテーブルを結合する場合
3つ以上のテーブルを結合する場合も、考え方は2つのときと同じです。テーブルを1つ追加するごとにJOINとONを1組ずつ書き足します。
【使用イメージ】
SELECT カラム名 FROM テーブルA JOIN テーブルB ON 結合条件1 JOIN テーブルC ON 結合条件2;順番にテーブルを結合します。
「体育館利用」テーブルを用いて、体育館を利用する運動部の生徒を抽出します。
「体育館利用(gym_use)」テーブル
| 部活ID(club_id) | 利用時間帯(time_zone) |
| 3 | 午前 |
| 4 | 午後 |
【実行コード】
SELECT student.name FROM student INNER JOIN sport_club ON student.club_id = sport_club.id INNER JOIN gym_use ON student.club_id = gym_use.club_id;【実行結果】
| 氏名 (name) |
| 鈴木 |
上記の例では1つ目のJOIN句で運動部の生徒を抽出し、2つ目のJOIN句で体育館を利用する部活に属する生徒を抽出しています。
なお、内部結合(INNER JOIN)だけを使う場合は、FROM句に書くテーブルの順番を入れ替えても結果は同じになります。どの順番で結合するかは、データベースが効率のよい方法を自動で選ぶためです(変わるのは、ORDER BYを指定していないときの行の並び順くらいです)。一方、外部結合(LEFT JOINなど)では、どのテーブルを基準にするかで結果が変わるため、順番に注意が必要です。
JOIN句の応用例
ここまでに紹介してきたJOIN句の使用例の他にも、いくつか応用的な使い方を紹介しましょう。
セルフ結合
同一のテーブルを別名で二度参照して、階層構造やデータ間の関連性などを抽出する方法です。同じテーブルを2回使うため、それぞれに別のエイリアス(下の例ではs1とs2)を付けて、どちらのテーブルの列なのかをデータベースが区別できるようにする必要があります。
データの構造を取得できるので、たとえば社員情報から上司・部下といった関係の情報を取得することもできます。
サンプルのテーブルを使って、同じ部活に所属している人の情報を取得してみましょう。
【実行クエリ】
SELECT s1.name AS 本人,
s2.name AS 同じ部活の人
FROM student s1
JOIN student s2
ON s1.club_id = s2.club_id
WHERE s1.number != s2.number;
【実行結果】
| 本人 | 同じ部活の人 |
| 山田 | 菊池 |
| 菊池 | 山田 |
USINGで共通のカラムを結合する
共通のカラム名があるときに使えるJOIN句の用法です。
【実行クエリ】
SELECT s.name AS 生徒名, g.time_zone AS 利用時間帯
FROM student s
JOIN gym_use g USING (club_id);「生徒(student)」テーブルと「体育館利用(gym_use)」テーブルは、どちらも「club_id」という同じ名前のカラムを持っているため、USING (club_id) で結合できます。
【実行結果】
| 生徒名 | 利用時間帯 |
| 鈴木 | 午前 |
| 田中 | 午後 |
この記法を用いると、JOINの結合条件の部分を “ON a.column = b.column” のようにしなくとも、省略した形でクエリを組み立てることができます。共通するカラム名は明示されているので、可読性の点でも安心です。なお、「生徒」テーブルの「club_id」と「運動部」テーブルの「id」のように、カラム名が異なる場合はUSINGを使えないため、ONで結合条件を書きます。
似た書き方に、同じ名前のカラムをすべて自動で結合条件にする「NATURAL JOIN」があります。ただし、意図しないカラムまで結合条件に含まれることがあるため注意が必要です。たとえば、多くのテーブルに「last_update」(最終更新日時)のような共通のカラムがあると、そのカラムまで一致する行しか結合されず、結果が0件になることもあります。結合に使うカラムが明確なUSINGやONを使うほうが安全です。
JOINに関するよくある質問
Q. 「JOIN」と「INNER JOIN」に違いはありますか?
A. ありません。単に「JOIN」と書いた場合は内部結合(INNER JOIN)として扱われます。同じように「LEFT JOIN」は「LEFT OUTER JOIN」、「RIGHT JOIN」は「RIGHT OUTER JOIN」の省略形です。ただし、結合にはいくつも種類があるため、あとからクエリを読む人のためにも「INNER JOIN」と明示して書くのがおすすめです。
Q. JOINとUNIONの違いは何ですか?
A. JOINはテーブルを横につなげて列を増やし、UNIONは2つのSELECT文の結果を縦につなげて行を増やします。UNIONについてはUNION句の解説記事で詳しく解説しています。
Q. 結合条件はONとWHEREのどちらに書けばよいですか?
A. テーブル同士を結びつける条件はON、結合した結果を絞り込む条件はWHEREに書くのが基本です。内部結合ではどちらに書いても結果は同じですが、外部結合では結果が変わります(左外部結合の解説を参照)。
Q. 外部キー制約がないとJOINできませんか?
A. できます。外部キー制約は、存在しない値が登録されないようにするための設定です。結合に使うカラムの値が対応していれば、制約がなくても結合できます。
SQL関連記事
- RDBを操作するデータベース言語「SQL」とは?
- RDB以外のデータベース管理システム「NoSQL」とは?
- RDB管理システム「MySQL」とは?
- RDB管理システム「PostgreSQL」とは?
- RDB管理システム「SQL Server」とは?
- RDB管理システム「SQLite」とは?
- ブラウザ上でMySQLを管理できる「phpMyAdmin」とは?
- データを抽出する「SELECT」文
- データを追加する「INSERT」文
- データを削除する「DELETE」文
- データを更新する「UPDATE」文
- 抽出条件を指定する「WHERE」句
- 抽出条件を指定する「HAVING」句
- 複数の抽出条件をまとめる「IN」句
- 重複レコードを除外する「DISTINCT」
- 抽出範囲を指定する「BETWEEN」演算子
- 抽出条件を満たすレコードの存在の有無を調べる「EXISTS」句
- レコード件数を取得する集計関数「COUNT」関数
- レコードをグループ化する「GROUP BY」句
- 抽出結果を並び替える「ORDER BY」句
- テーブル間のデータを結合する「JOIN」句
- 抽出結果を統合して表示する「UNION」句
- データの曖昧検索「LIKE」句
- 条件分岐処理「CASE」式
- 「NULL」の扱い方
SQLの勉強方法は?
書籍やインターネットで学習する方法があります。昨今では、YouTubeなどの動画サイトやエンジニアのコミュニティサイトなども充実していて多くの情報が手に入ります。
そして、より効率的に知識・スキルを習得するには、知識をつけながら実際に手を動かしてみるなど、インプットとアウトプットを繰り返していくことが重要です。特に独学の場合は、有識者に質問ができたりフィードバックをもらえるような環境があると、理解度が深まるでしょう。
ただ、SQLに限らず、ITスキルを身につける際、どうしても課題にぶつかってしまうことはありますよね。特に独学だと、わからない部分をプロに質問できる機会を確保しにくく、モチベーションが続きにくいという側面があります。独学でモチベーションを維持する自信がない人にはプログラミングスクールという手もあります。費用は掛かりますが、その分スキルを身につけやすいです。しっかりと知識・スキルを習得して実践に活かしたいという人はプログラミングスクールがおすすめです。
プログラミングスクールならテックマニアがおすすめ!
ITスキル需要の高まりとともにプログラミングスクールも増えました。しかし、どのスクールに通うべきか迷ってしまう人もいるでしょう。そんな方にはテックマニアをおすすめします!これまで多くのITエンジニアを育成・輩出してきたテックマニアでもプログラミングスクールを開講しています。
<テックマニアの特徴>
・たしかな育成実績と親身な教育 ~セカンドキャリアを全力支援~
・講師が現役エンジニア ~“本当”の開発ノウハウを直に学べる~
・専属講師が学習を徹底サポート ~「わからない」を徹底解決~
・実務ベースでスキルを習得 ~実践的な凝縮カリキュラム~
このような特徴を持つテックマニアはITエンジニアのスタートラインとして最適です。
話を聞きたい・詳しく知りたいという方はこちらからお気軽にお問い合わせください。
