Skip to main content

テーブルの結合

この章で得られるスキル:

  • ✅ JOINの必要性を説明できる
  • ✅ INNER JOINで2つのテーブルを結合できる
  • ✅ LEFT OUTER JOINで外部結合ができる
  • ✅ 自己結合ができる
  • ✅ 3つ以上のテーブルを結合できる
  • ✅ JOINとWHERE、GROUP BYを組み合わせられる

Step 0: まず体験してみよう

シナリオ:社員の名前と部門名を一緒に表示したい

前章で学んだ通り、テーブルは正規化によって分割されている。 社員テーブルには 部門ID しかなく、 部門名 は部門テーブルにある。

社員の名前と部門名を一緒に表示するにはどうすればよいか?

CREATE TABLE departments ( dept_id INTEGER PRIMARY KEY, dept_name VARCHAR(50) NOT NULL UNIQUE, location VARCHAR(50) ); CREATE TABLE employees ( emp_id INTEGER PRIMARY KEY, emp_name VARCHAR(50) NOT NULL, dept_id INTEGER REFERENCES departments(dept_id), hire_date DATE NOT NULL, salary INTEGER CHECK (salary > 0), email VARCHAR(100) UNIQUE ); INSERT INTO departments VALUES (1, '営業部', '東京'); INSERT INTO departments VALUES (2, '開発部', '大阪'); INSERT INTO departments VALUES (3, '人事部', '東京'); INSERT INTO departments VALUES (4, '広報部', '福岡'); INSERT INTO employees VALUES (1, '田中太郎', 1, '2020-04-01', 350000, 'tanaka@example.com'); INSERT INTO employees VALUES (2, '佐藤花子', 2, '2021-04-01', 400000, 'sato@example.com'); INSERT INTO employees VALUES (3, '鈴木一郎', 2, '2022-04-01', 320000, 'suzuki@example.com'); INSERT INTO employees VALUES (4, '高橋美咲', 3, '2023-04-01', 280000, 'takahashi@example.com'); INSERT INTO employees VALUES (5, '伊藤健太', 1, '2021-10-01', 330000, 'ito@example.com'); INSERT INTO employees VALUES (6, '渡辺あかり', NULL, '2024-01-15', 300000, 'watanabe@example.com'); -- 社員テーブルだけでは部門名がわからない SELECT emp_name, dept_id FROM employees; -- JOINで部門名も一緒に表示できる! SELECT e.emp_name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id = d.dept_id;

JOIN を使うと、分割されたテーブルを結合して必要な情報をまとめて取得できる。

ポイント

正規化でテーブルを分割するから、JOINで結合する必要がある。 「分割」と「結合」はセットで理解しよう。


Step 1: JOINの基本概念

なぜJOINが必要か

正規化によってテーブルを分割すると、関連するデータが複数のテーブルに散らばる。

社員テーブルの dept_id が、部門テーブルの dept_id への 外部キー になっている。 この外部キーが、テーブル結合の 橋渡し をする。

JOINの種類

種類説明結果
INNER JOIN両方のテーブルに一致するデータだけ取得一致しないデータは除外
LEFT OUTER JOIN左側テーブルの全データ + 一致する右側データ一致しない右側はNULL
RIGHT OUTER JOIN右側テーブルの全データ + 一致する左側データ一致しない左側はNULL
CROSS JOIN全ての組み合わせ(直積)めったに使わない

実務で最も使うのは INNER JOINLEFT OUTER JOIN である。


Step 2: INNER JOIN(内部結合)

基本構文

SELECT 列名
FROM テーブル1
INNER JOIN テーブル2 ON テーブル1.列名 = テーブル2.列名;

ON句 で結合条件を指定する。通常は外部キーと主キーの一致を条件にする。

CREATE TABLE departments ( dept_id INTEGER PRIMARY KEY, dept_name VARCHAR(50) NOT NULL UNIQUE, location VARCHAR(50) ); CREATE TABLE employees ( emp_id INTEGER PRIMARY KEY, emp_name VARCHAR(50) NOT NULL, dept_id INTEGER REFERENCES departments(dept_id), hire_date DATE NOT NULL, salary INTEGER CHECK (salary > 0), email VARCHAR(100) UNIQUE ); INSERT INTO departments VALUES (1, '営業部', '東京'); INSERT INTO departments VALUES (2, '開発部', '大阪'); INSERT INTO departments VALUES (3, '人事部', '東京'); INSERT INTO departments VALUES (4, '広報部', '福岡'); INSERT INTO employees VALUES (1, '田中太郎', 1, '2020-04-01', 350000, 'tanaka@example.com'); INSERT INTO employees VALUES (2, '佐藤花子', 2, '2021-04-01', 400000, 'sato@example.com'); INSERT INTO employees VALUES (3, '鈴木一郎', 2, '2022-04-01', 320000, 'suzuki@example.com'); INSERT INTO employees VALUES (4, '高橋美咲', 3, '2023-04-01', 280000, 'takahashi@example.com'); INSERT INTO employees VALUES (5, '伊藤健太', 1, '2021-10-01', 330000, 'ito@example.com'); INSERT INTO employees VALUES (6, '渡辺あかり', NULL, '2024-01-15', 300000, 'watanabe@example.com'); -- INNER JOIN:社員名と部門名を結合 SELECT e.emp_name, d.dept_name, d.location FROM employees e INNER JOIN departments d ON e.dept_id = d.dept_id;

INNER JOINの特徴

結果を確認すると、 渡辺あかり (dept_id が NULL)と 広報部 (社員がいない)は表示されない。 INNER JOINは 両方のテーブルに一致するデータだけ を返すためである。

やってみよう

上のコードのSELECT文を変更して、 e.salary も表示してみよう。


Step 3: テーブルの別名(エイリアス)

なぜ別名が必要か

JOINでは複数のテーブルに同じ名前の列がある場合がある。例えば、どちらのテーブルの dept_id かを明確にする必要がある。

-- テーブル名をそのまま書くと長い
SELECT employees.emp_name, departments.dept_name
FROM employees
INNER JOIN departments ON employees.dept_id = departments.dept_id;

-- 別名を使うと短く書ける
SELECT e.emp_name, d.dept_name
FROM employees e
INNER JOIN departments d ON e.dept_id = d.dept_id;

別名の付け方

書き方
テーブル名 AS 別名employees AS e
テーブル名 別名employees e

AS は省略可能である。実務ではどちらも使われるが、短い別名を使うのが一般的である。

注意

別名を付けたら、そのクエリ内ではテーブル名の代わりに別名を使う。 FROM employees e と書いた後で employees.emp_name と書くとエラーになる場合がある。


Step 4: LEFT OUTER JOIN(左外部結合)

INNER JOINでは取りこぼすデータがある

Step 2で見たように、INNER JOINは 一致しないデータを除外 する。 部門に所属していない社員や、社員のいない部門が結果に含まれない。

全社員を表示しつつ、部門名がわかる社員は部門名も表示したい という場合は LEFT OUTER JOIN を使う。

CREATE TABLE departments ( dept_id INTEGER PRIMARY KEY, dept_name VARCHAR(50) NOT NULL UNIQUE, location VARCHAR(50) ); CREATE TABLE employees ( emp_id INTEGER PRIMARY KEY, emp_name VARCHAR(50) NOT NULL, dept_id INTEGER REFERENCES departments(dept_id), hire_date DATE NOT NULL, salary INTEGER CHECK (salary > 0), email VARCHAR(100) UNIQUE ); INSERT INTO departments VALUES (1, '営業部', '東京'); INSERT INTO departments VALUES (2, '開発部', '大阪'); INSERT INTO departments VALUES (3, '人事部', '東京'); INSERT INTO departments VALUES (4, '広報部', '福岡'); INSERT INTO employees VALUES (1, '田中太郎', 1, '2020-04-01', 350000, 'tanaka@example.com'); INSERT INTO employees VALUES (2, '佐藤花子', 2, '2021-04-01', 400000, 'sato@example.com'); INSERT INTO employees VALUES (3, '鈴木一郎', 2, '2022-04-01', 320000, 'suzuki@example.com'); INSERT INTO employees VALUES (4, '高橋美咲', 3, '2023-04-01', 280000, 'takahashi@example.com'); INSERT INTO employees VALUES (5, '伊藤健太', 1, '2021-10-01', 330000, 'ito@example.com'); INSERT INTO employees VALUES (6, '渡辺あかり', NULL, '2024-01-15', 300000, 'watanabe@example.com'); -- INNER JOIN:部門未所属の渡辺あかりが表示されない SELECT e.emp_name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id = d.dept_id; -- LEFT JOIN:全社員が表示される(部門名がない場合はNULL) SELECT e.emp_name, d.dept_name FROM employees e LEFT OUTER JOIN departments d ON e.dept_id = d.dept_id;

INNER JOIN と LEFT JOIN の比較

結合方法渡辺あかり(部門なし)広報部(社員なし)
INNER JOIN❌ 表示されない❌ 表示されない
LEFT JOIN(employees が左)✅ 部門名はNULL❌ 表示されない
LEFT JOIN(departments が左)❌ 表示されない✅ 社員名はNULL
ポイント

LEFT OUTER JOINLEFT JOIN と省略して書ける。実務ではこの省略形の方がよく使われる。

NULLを使って「一致しないデータ」を見つける

LEFT JOINとIS NULLを組み合わせると、 どの部門にも所属していない社員 を見つけられる。

CREATE TABLE departments ( dept_id INTEGER PRIMARY KEY, dept_name VARCHAR(50) NOT NULL UNIQUE, location VARCHAR(50) ); CREATE TABLE employees ( emp_id INTEGER PRIMARY KEY, emp_name VARCHAR(50) NOT NULL, dept_id INTEGER REFERENCES departments(dept_id), hire_date DATE NOT NULL, salary INTEGER CHECK (salary > 0), email VARCHAR(100) UNIQUE ); INSERT INTO departments VALUES (1, '営業部', '東京'); INSERT INTO departments VALUES (2, '開発部', '大阪'); INSERT INTO departments VALUES (3, '人事部', '東京'); INSERT INTO departments VALUES (4, '広報部', '福岡'); INSERT INTO employees VALUES (1, '田中太郎', 1, '2020-04-01', 350000, 'tanaka@example.com'); INSERT INTO employees VALUES (2, '佐藤花子', 2, '2021-04-01', 400000, 'sato@example.com'); INSERT INTO employees VALUES (3, '鈴木一郎', 2, '2022-04-01', 320000, 'suzuki@example.com'); INSERT INTO employees VALUES (4, '高橋美咲', 3, '2023-04-01', 280000, 'takahashi@example.com'); INSERT INTO employees VALUES (5, '伊藤健太', 1, '2021-10-01', 330000, 'ito@example.com'); INSERT INTO employees VALUES (6, '渡辺あかり', NULL, '2024-01-15', 300000, 'watanabe@example.com'); -- 部門に所属していない社員を見つける SELECT e.emp_name FROM employees e LEFT JOIN departments d ON e.dept_id = d.dept_id WHERE d.dept_id IS NULL; -- 社員がいない部門を見つける SELECT d.dept_name FROM departments d LEFT JOIN employees e ON d.dept_id = e.dept_id WHERE e.emp_id IS NULL;

Step 5: 自己結合

同じテーブル同士を結合する

社員テーブルに「上司ID」があるケースを考える。上司も社員テーブルに存在する。 このように、 同じテーブルを2回参照 して結合するのが 自己結合 である。

CREATE TABLE employees_with_manager ( emp_id INTEGER PRIMARY KEY, emp_name VARCHAR(50) NOT NULL, manager_id INTEGER REFERENCES employees_with_manager(emp_id), salary INTEGER CHECK (salary > 0) ); INSERT INTO employees_with_manager VALUES (1, '山本部長', NULL, 600000); INSERT INTO employees_with_manager VALUES (2, '田中課長', 1, 450000); INSERT INTO employees_with_manager VALUES (3, '佐藤花子', 2, 400000); INSERT INTO employees_with_manager VALUES (4, '鈴木一郎', 2, 320000); INSERT INTO employees_with_manager VALUES (5, '高橋美咲', 1, 350000); -- 自己結合:社員名と上司名を表示 SELECT e.emp_name AS 社員名, m.emp_name AS 上司名 FROM employees_with_manager e LEFT JOIN employees_with_manager m ON e.manager_id = m.emp_id;

自己結合のポイント

  • 同じテーブルを2回使うので、 別名が必須 である
  • 上の例では e(社員として)と m(上司として)の2つの別名を付けている
  • 山本部長の manager_id は NULL なので、LEFT JOINを使って上司名を NULL として表示している
やってみよう

上のコードを修正して、上司がいない社員(= 最上位の管理者)だけを表示してみよう。 ヒント:WHERE m.emp_id IS NULL を追加する。


Step 6: 3つ以上のテーブルの結合

JOINを連続して書く

実務では、3つ以上のテーブルを結合することも多い。 JOINを連続して書くことで、複数のテーブルをつなげられる。

CREATE TABLE departments ( dept_id INTEGER PRIMARY KEY, dept_name VARCHAR(50) NOT NULL UNIQUE, location VARCHAR(50) ); CREATE TABLE employees ( emp_id INTEGER PRIMARY KEY, emp_name VARCHAR(50) NOT NULL, dept_id INTEGER REFERENCES departments(dept_id), hire_date DATE NOT NULL, salary INTEGER CHECK (salary > 0) ); CREATE TABLE projects ( project_id INTEGER PRIMARY KEY, project_name VARCHAR(100) NOT NULL, start_date DATE ); CREATE TABLE assignments ( emp_id INTEGER REFERENCES employees(emp_id), project_id INTEGER REFERENCES projects(project_id), role VARCHAR(50), PRIMARY KEY (emp_id, project_id) ); INSERT INTO departments VALUES (1, '営業部', '東京'); INSERT INTO departments VALUES (2, '開発部', '大阪'); INSERT INTO departments VALUES (3, '人事部', '東京'); INSERT INTO employees VALUES (1, '田中太郎', 1, '2020-04-01', 350000); INSERT INTO employees VALUES (2, '佐藤花子', 2, '2021-04-01', 400000); INSERT INTO employees VALUES (3, '鈴木一郎', 2, '2022-04-01', 320000); INSERT INTO employees VALUES (4, '高橋美咲', 3, '2023-04-01', 280000); INSERT INTO projects VALUES (1, 'ECサイト開発', '2024-01-01'); INSERT INTO projects VALUES (2, '社内システム改修', '2024-04-01'); INSERT INTO assignments VALUES (1, 1, 'リーダー'); INSERT INTO assignments VALUES (2, 1, 'メンバー'); INSERT INTO assignments VALUES (3, 1, 'メンバー'); INSERT INTO assignments VALUES (2, 2, 'リーダー'); INSERT INTO assignments VALUES (4, 2, 'メンバー'); -- 3テーブル結合:社員名、部門名、プロジェクト名を表示 SELECT e.emp_name, d.dept_name, p.project_name, a.role FROM employees e INNER JOIN departments d ON e.dept_id = d.dept_id INNER JOIN assignments a ON e.emp_id = a.emp_id INNER JOIN projects p ON a.project_id = p.project_id ORDER BY p.project_name, a.role;

テーブル間の関係

assignments は社員とプロジェクトの 中間テーブル である。 社員とプロジェクトは 多対多 の関係にあり、中間テーブルを介して結合する(詳しくは第9章で学ぶ)。


Step 7: JOINとWHERE、GROUP BYの組み合わせ

JOINした結果をWHEREで絞り込む

JOINで結合した結果に対して、WHEREで条件を指定できる。

CREATE TABLE departments ( dept_id INTEGER PRIMARY KEY, dept_name VARCHAR(50) NOT NULL UNIQUE, location VARCHAR(50) ); CREATE TABLE employees ( emp_id INTEGER PRIMARY KEY, emp_name VARCHAR(50) NOT NULL, dept_id INTEGER REFERENCES departments(dept_id), hire_date DATE NOT NULL, salary INTEGER CHECK (salary > 0) ); INSERT INTO departments VALUES (1, '営業部', '東京'); INSERT INTO departments VALUES (2, '開発部', '大阪'); INSERT INTO departments VALUES (3, '人事部', '東京'); INSERT INTO employees VALUES (1, '田中太郎', 1, '2020-04-01', 350000); INSERT INTO employees VALUES (2, '佐藤花子', 2, '2021-04-01', 400000); INSERT INTO employees VALUES (3, '鈴木一郎', 2, '2022-04-01', 320000); INSERT INTO employees VALUES (4, '高橋美咲', 3, '2023-04-01', 280000); INSERT INTO employees VALUES (5, '伊藤健太', 1, '2021-10-01', 330000); INSERT INTO employees VALUES (6, '渡辺あかり', 2, '2023-07-01', 300000); -- JOINとWHERE:東京勤務の社員を表示 SELECT e.emp_name, d.dept_name, d.location FROM employees e INNER JOIN departments d ON e.dept_id = d.dept_id WHERE d.location = '東京'; -- JOINとGROUP BY:部門ごとの社員数と平均給与 SELECT d.dept_name, COUNT(*) AS 社員数, ROUND(AVG(e.salary)) AS 平均給与 FROM employees e INNER JOIN departments d ON e.dept_id = d.dept_id GROUP BY d.dept_name ORDER BY 平均給与 DESC; -- JOINとGROUP BY + HAVING:社員が2人以上の部門 SELECT d.dept_name, COUNT(*) AS 社員数 FROM employees e INNER JOIN departments d ON e.dept_id = d.dept_id GROUP BY d.dept_name HAVING COUNT(*) >= 2;

SQLの実行順序(JOIN含む)

JOINを含むSQLの実行順序は以下の通りである。

順番処理内容
1FROM + JOINテーブルを結合する
2WHERE行を絞り込む
3GROUP BYグループ化する
4HAVINGグループを絞り込む
5SELECT列を選択・計算する
6ORDER BY並び替える
7LIMIT件数を制限する
注意

JOINはWHEREよりも先に実行される。そのため、 結合条件はON句に書き、絞り込み条件はWHEREに書く のが正しい使い分けである。


Step 8: 実践課題

課題1:社員の詳細情報を表示しよう

社員名、部門名、プロジェクト名、役割を一覧表示するSQLを書こう。 プロジェクトに参加していない社員も表示すること(LEFT JOINを使う)。

課題2:部門ごとの集計を表示しよう

部門名ごとの社員数と平均給与を表示するSQLを書こう。 社員がいない部門も表示すること。

課題3:上司と部下の関係を表示しよう

自己結合を使って、各社員とその上司の名前を表示するSQLを書こう。

CREATE TABLE departments ( dept_id INTEGER PRIMARY KEY, dept_name VARCHAR(50) NOT NULL UNIQUE, location VARCHAR(50) ); CREATE TABLE employees ( emp_id INTEGER PRIMARY KEY, emp_name VARCHAR(50) NOT NULL, dept_id INTEGER REFERENCES departments(dept_id), manager_id INTEGER REFERENCES employees(emp_id), hire_date DATE NOT NULL, salary INTEGER CHECK (salary > 0) ); CREATE TABLE projects ( project_id INTEGER PRIMARY KEY, project_name VARCHAR(100) NOT NULL ); CREATE TABLE assignments ( emp_id INTEGER REFERENCES employees(emp_id), project_id INTEGER REFERENCES projects(project_id), role VARCHAR(50), PRIMARY KEY (emp_id, project_id) ); INSERT INTO departments VALUES (1, '営業部', '東京'); INSERT INTO departments VALUES (2, '開発部', '大阪'); INSERT INTO departments VALUES (3, '人事部', '東京'); INSERT INTO departments VALUES (4, '広報部', '福岡'); INSERT INTO employees VALUES (1, '山本部長', 1, NULL, '2015-04-01', 600000); INSERT INTO employees VALUES (2, '田中課長', 2, 1, '2018-04-01', 450000); INSERT INTO employees VALUES (3, '佐藤花子', 2, 2, '2021-04-01', 400000); INSERT INTO employees VALUES (4, '鈴木一郎', 2, 2, '2022-04-01', 320000); INSERT INTO employees VALUES (5, '高橋美咲', 3, 1, '2023-04-01', 280000); INSERT INTO employees VALUES (6, '渡辺あかり', NULL, NULL, '2024-01-15', 300000); INSERT INTO projects VALUES (1, 'ECサイト開発'); INSERT INTO projects VALUES (2, '社内システム改修'); INSERT INTO assignments VALUES (2, 1, 'リーダー'); INSERT INTO assignments VALUES (3, 1, 'メンバー'); INSERT INTO assignments VALUES (4, 1, 'メンバー'); INSERT INTO assignments VALUES (2, 2, 'リーダー'); INSERT INTO assignments VALUES (5, 2, 'メンバー'); -- 課題1:社員名、部門名、プロジェクト名、役割を表示 -- プロジェクト未参加の社員もLEFT JOINで表示すること -- ここにSQLを書こう -- 課題2:部門名ごとの社員数と平均給与を表示 -- 社員がいない部門も表示すること -- ここにSQLを書こう -- 課題3:各社員とその上司の名前を表示(自己結合) -- ここにSQLを書こう SELECT 'ここに課題1〜3のSQLを書いてみよう' AS message;

まとめ

この章では、 テーブルの結合(JOIN) について学んだ。

🎯 達成できたこと

  • ✅ JOINの必要性を説明できる
  • ✅ INNER JOINで2つのテーブルを結合できる
  • ✅ テーブルに別名を付けられる
  • ✅ LEFT OUTER JOINで外部結合ができる
  • ✅ 自己結合ができる
  • ✅ 3つ以上のテーブルを結合できる
  • ✅ JOINとWHERE、GROUP BYを組み合わせられる

📚 学んだ内容

  • テーブルを正規化(分割)したから、JOINで結合する必要がある
  • INNER JOINは両方のテーブルに一致するデータだけを返す
  • LEFT JOINは左側テーブルの全データを返し、一致しない右側はNULLになる
  • 自己結合は同じテーブルを2回参照して結合する(別名が必須)
  • JOINはWHERE、GROUP BY、HAVINGと組み合わせて使える

🚀 次のステップ

次の章では、 ER図とデータベース設計 について学ぶ。 テーブルの関係を視覚的に表現する方法と、設計のプロセスを学ぶ。


💡 よくある質問

Q1: INNER JOINとLEFT JOINのどちらを使えばいい?

A: 「一致しないデータも表示したいか」で判断する。例えば「全社員を表示したいが、部門名がない場合もある」ならLEFT JOIN、「部門に所属している社員だけでよい」ならINNER JOINを使う。迷ったらLEFT JOINの方が安全(データの取りこぼしがない)である。

Q2: RIGHT JOINは使わないの?

A: RIGHT JOINはLEFT JOINの左右を入れ替えたものであり、FROMに書くテーブルの順序を変えればLEFT JOINで代用できる。読みやすさのため、LEFT JOINに統一するのが一般的である。

Q3: JOINの結合条件を間違えるとどうなる?

A: 結合条件が不適切だと 直積(CROSS JOIN)のような結果になり、意図しない大量のデータが返される。例えば社員5人と部門3つでON句なしにJOINすると15行(5×3)になる。必ずON句で正しい結合条件を指定しよう。

Q4: JOINを使うとパフォーマンスは大丈夫?

A: 適切なインデックス(特に外部キー列)があれば、JOINのパフォーマンスは十分に速い。数万件程度のデータであれば、JOINを3〜4つ連続しても問題ない。パフォーマンスが気になるのは数百万件以上のデータを扱う場合である。

Q5: AIにJOINのSQLを書かせてもいい?

A: 複雑なJOINはAIに書かせるのが効率的である。ただし、INNER JOINとLEFT JOINの使い分けが要件に合っているか、結合条件が正しいかは必ずレビューしよう。特に「NULLのデータを含めるかどうか」は要件次第なので、AIに正確に伝える必要がある。


練習問題

この章の内容を理解できたか確認しよう。

テーブルの結合(JOIN)が必要な理由として正しいものを選べ。

正解

B. 正規化によって分割されたテーブルの関連データをまとめて取得するため

解説

正規化によってデータは複数のテーブルに分割されている。 JOINはその分割されたテーブルのデータをまとめて取得するための操作である。

具体例employees テーブルには dept_id しかなく、部門名(dept_name)は departments テーブルにある。 「社員名と所属部門名」を一度に取得したい場合、2つのテーブルをJOINする必要がある。

SELECT e.emp_name, d.dept_name
FROM employees e
INNER JOIN departments d ON e.dept_id = d.dept_id;

JOINの種類

  • INNER JOIN: 両テーブルに一致する行のみ取得
  • LEFT JOIN: 左テーブルの全行を取得(右テーブルに一致なければNULL)
  • RIGHT JOIN: 右テーブルの全行を取得(左テーブルに一致なければNULL)
  • FULL OUTER JOIN: 両テーブルの全行を取得

SQL記述の約束
  • テーブル名・カラム名は 小文字 で入力する(例: employees, dept_id
  • SQLキーワードは 大文字 で入力する(例: SELECT, FROM, WHERE

正しい例: SELECT emp_name FROM employees WHERE dept_id = 1;

employees テーブルと departments テーブルを dept_id で内部結合し、 emp_namedept_name を取得するSQL文を完成させよ。

SELECT e.emp_name, d.dept_name FROM employees e
departments d ON e.dept_id = d.dept_id;

解答例
SELECT e.emp_name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id = d.dept_id;
解説

INNER JOIN テーブル名 ON 結合条件 で内部結合を行う。

基本構文:

SELECT テーブル別名1.列名, テーブル別名2.列名
FROM テーブル名1 別名1
INNER JOIN テーブル名2 別名2 ON 別名1.外部キー = 別名2.主キー;

INNER JOIN の特性

  • 両テーブルで結合条件が一致する行のみを返す
  • 一方のテーブルに対応する行がない場合はその行が除外される
  • employees の中で dept_id=NULL渡辺あかり は INNER JOIN では除外される

ON 句

  • 外部キーと参照先の主キーを等号(=)で結ぶのが基本パターン
  • ON e.dept_id = d.dept_id: 社員の部門IDと部門テーブルの部門IDが一致する行を結合

SQL記述の約束
  • テーブル名・カラム名は 小文字 で入力する(例: employees, dept_id
  • SQLキーワードは 大文字 で入力する(例: SELECT, FROM, WHERE

正しい例: SELECT emp_name FROM employees WHERE dept_id = 1;

employees テーブルに e という別名を付けて、emp_name を取得するSQL文を完成させよ。

SELECT
.emp_name FROM employees
;

解答例
SELECT e.emp_name FROM employees e;
解説

テーブルに別名(エイリアス)を付けるには FROM テーブル名 別名 と記述する。

基本構文:

SELECT 別名.列名 FROM テーブル名 別名;

テーブル別名の用途

  • JOINで同名の列(例: dept_id)が複数テーブルにある場合に区別できる
  • 長いテーブル名を短縮して記述量を減らせる
  • 自己結合(同じテーブルを2回JOIN)では必須

命名規則

  • 慣例としてテーブル名の頭文字1〜2文字を使うことが多い(例: employeesedepartmentsd
  • AS を付けて employees AS e と書くこともできる

JOINを使うときはテーブル別名を付けるのが一般的である。

SQL記述の約束
  • テーブル名・カラム名は 小文字 で入力する(例: employees, dept_id
  • SQLキーワードは 大文字 で入力する(例: SELECT, FROM, WHERE

正しい例: SELECT emp_name FROM employees WHERE dept_id = 1;

部門未所属の社員も含め、全社員の emp_namedept_name を取得するSQL文を完成させよ。 (部門がない場合は dept_name がNULLとなる)

SELECT e.emp_name, d.dept_name FROM employees e
JOIN departments d ON e.dept_id = d.dept_id;

部門未所属の社員も含めるには INNER ではなく何が必要か考えよう

解答例
SELECT e.emp_name, d.dept_name FROM employees e LEFT JOIN departments d ON e.dept_id = d.dept_id;
解説

LEFT JOIN(または LEFT OUTER JOIN)は左テーブルの全行を取得し、 右テーブルに一致がない場合はNULLを返す。

基本構文:

SELECT 列名
FROM 左テーブル e
LEFT JOIN 右テーブル d ON 結合条件;

INNER JOIN との違い

  • INNER JOIN: 両テーブルに一致する行のみ(渡辺あかりは除外される)
  • LEFT JOIN: 左テーブル(employees)の全行(渡辺あかりも含まれ、dept_nameはNULL)

結果イメージ

| emp_name   | dept_name |
|------------|-----------|
| 田中太郎   | 営業部    |
| 鈴木花子   | 開発部    |
| 佐藤次郎   | 開発部    |
| 渡辺あかり | NULL      |  ← LEFT JOIN なので含まれる
| 中村健一   | 営業部    |

INNER JOINLEFT JOIN の違いとして正しいものを選べ。

正解

A. INNER JOIN は両テーブルに一致する行のみ取得し、LEFT JOIN は左テーブルの全行を取得する(右テーブルに一致がなければNULL)

解説

INNER JOIN(内部結合):

  • 両テーブルで結合条件が一致する行のみを返す
  • 一方に対応する行がなければその行は除外される
  • employees の中で dept_id=NULL の社員(渡辺あかり)は除外される

LEFT JOIN(左外部結合):

  • 左テーブル(FROM に書いたテーブル)の全行を返す
  • 右テーブルに一致がない場合は NULL で埋める
  • 渡辺あかりも結果に含まれ、dept_nameNULL になる

使い分け

  • 関連データが必ず存在する場合 → INNER JOIN
  • 関連データが存在しない行も含めたい場合 → LEFT JOIN
  • 例: 「全社員と、所属部門名(未所属の場合はNULL)を取得」→ LEFT JOIN

SQL記述の約束
  • テーブル名・カラム名は 小文字 で入力する(例: employees, dept_id
  • SQLキーワードは 大文字 で入力する(例: SELECT, FROM, WHERE

正しい例: SELECT emp_name FROM employees WHERE dept_id = 1;

employees テーブルを e1e2 の2つの別名で自己結合し、 同じ部門に所属する社員のペアを取得するSQL文を完成させよ。 (e1.emp_id < e2.emp_id で重複の組み合わせを除去する)

SELECT e1.emp_name AS 社員1, e2.emp_name AS 社員2 FROM employees
INNER JOIN employees e2 ON e1.dept_id = e2.dept_id AND e1.emp_id < e2.emp_id;

同じテーブルを2つの別名で使うのが自己結合のポイント

解答例
SELECT e1.emp_name AS 社員1, e2.emp_name AS 社員2 FROM employees e1 INNER JOIN employees e2 ON e1.dept_id = e2.dept_id AND e1.emp_id < e2.emp_id;
解説

自己結合は同じテーブルを異なる別名で複数回参照する結合である。

基本構文:

SELECT e1.列名, e2.列名
FROM テーブル名 e1
INNER JOIN テーブル名 e2 ON 結合条件;

自己結合の用途

  • 社員同士の関係(同じ部門に所属する社員のペアなど)
  • 階層構造(上司・部下の関係)
  • 同一テーブル内の比較

重複排除の条件e1.emp_id < e2.emp_id という条件で

  • (田中, 中村) のペアのみ返す
  • (中村, 田中) の逆順の重複を除外する

この条件がないと同じペアが2回表示されてしまう。

SQL記述の約束
  • テーブル名・カラム名は 小文字 で入力する(例: employees, dept_id
  • SQLキーワードは 大文字 で入力する(例: SELECT, FROM, WHERE

正しい例: SELECT emp_name FROM employees WHERE dept_id = 1;

employeesdepartmentsassignments の3テーブルを結合して、 社員名・部門名・担当プロジェクトIDを取得するSQL文を完成させよ。

SELECT e.emp_name, d.dept_name, a.project_id FROM employees e INNER JOIN departments d ON e.dept_id = d.dept_id
assignments a ON
.emp_id = a.emp_id;

3テーブルの結合はINNER JOINを2回連続して書く

解答例
SELECT e.emp_name, d.dept_name, a.project_id FROM employees e INNER JOIN departments d ON e.dept_id = d.dept_id INNER JOIN assignments a ON e.emp_id = a.emp_id;
解説

3つ以上のテーブルを結合するには INNER JOIN ... ON ... を連続して記述する。

基本構文:

FROM テーブル1 t1
INNER JOIN テーブル2 t2 ON t1.外部キー = t2.主キー
INNER JOIN テーブル3 t3 ON t1.外部キー = t3.主キー;
-- または t2.外部キー = t3.主キー(結合元は状況による)

各結合条件

  • employees JOIN departments: e.dept_id = d.dept_id(社員の所属部門)
  • employees JOIN assignments: e.emp_id = a.emp_id(社員のアサインメント)

注意点

  • JOINON 条件に正しいテーブル別名と列名を使う
  • 同名の列が複数テーブルに存在する場合は必ず 別名.列名 で区別する
  • assignmentsemployees と直接関係しているため e.emp_id = a.emp_id が結合条件

SQL記述の約束
  • テーブル名・カラム名は 小文字 で入力する(例: employees, dept_id
  • SQLキーワードは 大文字 で入力する(例: SELECT, FROM, WHERE

正しい例: SELECT emp_name FROM employees WHERE dept_id = 1;

部門名(dept_name)と部門ごとの社員数を取得するSQL文を完成させよ。

SELECT d.dept_name, COUNT(*) AS 社員数 FROM departments d INNER JOIN employees e ON d.dept_id = e.dept_id
d.
;

JOIN後のデータをグループ集計するには GROUP BY を使う

解答例
SELECT d.dept_name, COUNT(*) AS 社員数 FROM departments d INNER JOIN employees e ON d.dept_id = e.dept_id GROUP BY d.dept_name;
解説

JOINGROUP BY を組み合わせることで、結合後のデータをグループ集計できる。

SQL句の実行順序FROMJOINWHEREGROUP BYHAVINGSELECTORDER BYLIMIT

GROUP BY に使用できる列

  • GROUP BY に指定した列のみ SELECT に書ける(集約関数以外)
  • d.dept_nameGROUP BY しているため SELECTd.dept_name を書ける

注意点

  • GROUP BY d.dept_id でも正しいが、SELECT 句に dept_name を表示したいため dept_name でグループ化する
  • INNER JOIN なので部門に所属する社員のみ数えられる
  • LEFT JOIN に変えると社員が0人の部門も結果に含まれる

SQL記述の約束
  • テーブル名・カラム名は 小文字 で入力する(例: employees, dept_id
  • SQLキーワードは 大文字 で入力する(例: SELECT, FROM, WHERE

正しい例: SELECT emp_name FROM employees WHERE dept_id = 1;

「開発部(dept_name='開発部')に所属し、かつプロジェクトにアサインされている社員の名前と役割を取得せよ」 を実装するSQL文を完成させよ。

SELECT e.emp_name, a.role FROM employees e INNER JOIN departments d ON e.dept_id = d.dept_id
assignments a ON e.emp_id = a.emp_id
d.dept_name = '開発部';

3テーブルをJOINした後にWHEREで絞り込む順序を考えよう

解答例
SELECT e.emp_name, a.role FROM employees e INNER JOIN departments d ON e.dept_id = d.dept_id INNER JOIN assignments a ON e.emp_id = a.emp_id WHERE d.dept_name = '開発部';
解説

複数テーブルのJOINと WHERE による絞り込みを組み合わせることで、 複雑な条件のデータを取得できる。

この問題の解法

  1. 「アサインされている社員」→ employeesassignments を INNER JOIN
  2. 「開発部に所属」→ employeesdepartments を INNER JOIN して WHERE dept_name = '開発部'

INNER JOIN の自動フィルタリング

  • INNER JOIN assignments a ON e.emp_id = a.emp_idassignments に存在する社員のみを返す(アサインされていない社員は除外)
  • これにより「アサインされている社員のみ」が自動的に絞り込まれる

JOINとWHEREの組み合わせ: JOINで関連テーブルを結合した後、WHEREで条件を絞り込む。 JOINはテーブルを繋ぐ、WHEREは結果を絞り込む、という役割分担を意識すると良い。