こんにちは!「LINUX工房」管理人の「リナックス先生」です。
「MySQL初心者講座」の第5回へようこそ。前回までは1つのテーブルに対する検索(SELECT)や条件指定(WHERE)を学んできましたが、今日からは「複数のテーブルを同時に扱う」という、RDBMS(リレーショナルデータベース)の最も強力な機能に足を踏み入れます。
第2回の「テーブル設計」の回で、私たちはデータの重複を防ぐために「ユーザー情報」と「注文履歴」のようなデータを別々のテーブルに分割(正規化)しました。しかし、実際のWebサイトの画面、例えば「マイページ」には、ユーザーの名前と一緒に注文した商品の履歴が並んで表示されていますよね。別々のテーブルにある情報を、どうやって1つの画面に出しているのでしょうか?
その答えが、今回学習する「JOIN(テーブル結合)」です。
先生、テーブルを分けたのはいいんですが、プログラムからデータを取るときに「ユーザーを取るSQL」と「注文履歴を取るSQL」を別々に発行して、後からPHPで無理やりくっつけてました……。これってすごく面倒だし、処理が遅くなっている気がします。
コウ君、それは「N+1問題」と呼ばれる、Web開発で最もやってはいけないパフォーマンス低下の典型例よ!
データベースの中でデータをくっつけて(JOINして)からプログラムに渡すのが、プロの鉄則。最初は少し構文が長く感じるかもしれないけれど、一度理解すれば一生使える強力な魔法よ。図解も交えて、基礎からしっかり教えてあげるわ!
📚 MySQL初心者講座・連載ロードマップ(全8回)
- 【第1回】データベースの概念と最速インストール・初期設定
- 【第2回】テーブル設計の極意:データ型と主キーの重要性
- 【第3回】SQL基本4操作(CRUD):SELECT/INSERT/UPDATE/DELETEをマスターする
- 【第4回】データの抽出と並び替え:WHERE句とORDER BYを使いこなす
- 【第5回】複数のテーブルを繋ぐ:JOIN(結合)の魔法を理解する(本記事)
- 【第6回】集計とグループ化:COUNT/SUMとGROUP BYで統計を取る(公開予定)
- 【第7回】インデックスとパフォーマンス:重いクエリを劇的に速くする方法(公開予定)
- 【第8回】バックアップと復旧:mysqldumpによるデータ保護と自動化(公開予定)
目次
1. なぜテーブルを結合するのか?(リレーションの仕組み)
具体的なSQL文を見る前に、前提となるテーブル構成を定義しましょう。ここでは、ブログシステムを想定し、「ユーザー(users)テーブル」と、そのユーザーが書いた「記事(articles)テーブル」の2つを用意します。
1-1. usersテーブル(ユーザー情報)
| id (主キー) | name | |
|---|---|---|
| 1 | リナックス先生 | sensei@… |
| 2 | コウ君 | kou@… |
| 3 | 初心者さん | newbie@… |
1-2. articlesテーブル(記事情報)
| id (主キー) | user_id (外部キー) | title |
|---|---|---|
| 101 | 1 | Apacheの基礎 |
| 102 | 1 | MySQLの基礎 |
| 103 | 2 | 初めてのLinux |
ここで注目すべきは、articles テーブルにある user_id というカラムです。
この記事を「誰が書いたか」という情報は、名前の文字列(リナックス先生など)を直接書くのではなく、「usersテーブルのid(1や2)」を記録しています。このように、別のテーブルの主キーを参照するカラムのことを「外部キー(Foreign Key)」と呼びます。
JOINとは、この「主キー」と「外部キー」を接着剤にして、2つのテーブルを横にくっつける処理のことを指します。
2. 結合の王道:INNER JOIN(内部結合)の基本
最も頻繁に使われるのが INNER JOIN(インナージョイン:内部結合) です。これは「両方のテーブルに一致するデータが存在するものだけを抽出する」という結合方法です。
2-1. INNER JOINの構文と動作
「記事のタイトル」と「それを書いたユーザーの名前」を同時に取得するSQLを書いてみましょう。
SELECT
users.name,
articles.title
FROM
users
INNER JOIN
articles
ON
users.id = articles.user_id;
2-2. 構文の解説(ON句の重要性)
- FROM users: ベースとなるテーブルを指定します。
- INNER JOIN articles: くっつけたいテーブルを指定します。
- ON users.id = articles.user_id: ここが最も重要です。「どのような条件でくっつけるか(接着剤)」を指定します。usersテーブルの
idと、articlesテーブルのuser_idが一致する行同士を結合しなさい、という命令になります。
2-3. 実行結果
| name (usersから) | title (articlesから) |
|---|---|
| リナックス先生 | Apacheの基礎 |
| リナックス先生 | MySQLの基礎 |
| コウ君 | 初めてのLinux |
ここで注意深く見てください。usersテーブルに存在していた id=3 の「初心者さん」は、記事を1件も書いていない(articlesテーブルに対応するデータがない)ため、結果から完全に消滅してしまいました。 これがINNER JOINの特性です。「両方に存在するもの」しか出力されません。
3. データ欠損を防ぐ命綱:LEFT JOIN(左外部結合)
「記事を書いていないユーザーも含めて、全員の一覧を出したい。記事を書いていない人の記事タイトル欄は『空欄』でいい」
実際のWebサービス(管理画面のユーザー一覧など)では、このような要件のほうが圧倒的に多くなります。ここで使うのが LEFT JOIN(レフトジョイン:左外部結合) です。
3-1. LEFT JOINの構文
SELECT
users.name,
articles.title
FROM
users -- これが「左」のテーブル
LEFT JOIN
articles -- これが「右」のテーブル
ON
users.id = articles.user_id;
書き方はINNER JOINと全く同じで、キーワードが変わっただけです。しかし、結果は劇的に変わります。
3-2. 実行結果とNULLの登場
| name (usersから) | title (articlesから) |
|---|---|
| リナックス先生 | Apacheの基礎 |
| リナックス先生 | MySQLの基礎 |
| コウ君 | 初めてのLinux |
| 初心者さん | NULL |
LEFT JOINは、「FROMで指定したベースとなるテーブル(左側)のデータは絶対にすべて残す」という命令です。右側のテーブル(articles)に一致するデータがなかった場合、その部分は NULL(空っぽ) として埋め合わせて表示してくれます。
初心者のうちは、「データが消えてしまうのを防ぐために、基本はLEFT JOINを使う」と覚えておいても良いくらい、頻出するテクニックです。
4. RIGHT JOINとFULL OUTER JOIN(知識としての補足)
LEFT JOINがあるなら、当然 RIGHT JOIN(右外部結合) も存在します。これは基準を右側のテーブルにするだけですが、人間は左から右へ文字を読むため、「FROMで書いたメインのテーブルを基準にしてLEFT JOINで繋ぐ」という書き方で統一するのがプロの世界では一般的です。RIGHT JOINは可読性が下がるため、実務で使うことはほぼありません。
絵で見てわかるLinuxカーネルの仕組み 【電子書籍】[ 市川 正美 ] 価格:3058円 |
また、両方のテーブルのデータをすべて残す「FULL OUTER JOIN(完全外部結合)」という概念もありますが、実はMySQLはこの構文を直接サポートしていません(LEFT JOINとRIGHT JOINをUNIONで繋いで代用します)。知識として知っておく程度で問題ありません。
5. エイリアス(AS)を使ったスマートなSQLの書き方
テーブル名が長くなると、articles.user_id のように毎回書くのは非常に手間ですし、SQL文が読みにくくなります。そこで、テーブルに「あだ名(エイリアス)」をつけます。
SELECT
u.name,
a.title
FROM
users AS u -- usersテーブルを「u」と呼ぶ
LEFT JOIN
articles AS a -- articlesテーブルを「a」と呼ぶ
ON
u.id = a.user_id;
AS は省略可能なので、単に users u と書くエンジニアも多いです。複数のテーブルを結合する際は、このエイリアス表記が必須スキルとなります。
6. 3つ以上のテーブルを数珠繋ぎにする高度な結合
実際のシステムでは、「ユーザー」「記事」「カテゴリ」など、3つ以上のテーブルを結合することが当たり前になります。書き方は難しくありません。JOINを下に繋げていくだけです。
-- ユーザー、記事、カテゴリの3テーブルを結合する
SELECT
u.name AS '筆者名',
a.title AS '記事タイトル',
c.category_name AS 'カテゴリ'
FROM
users u
INNER JOIN
articles a ON u.id = a.user_id
LEFT JOIN
categories c ON a.category_id = c.id
WHERE
u.age >= 20;
ポイント: JOINした結果に対しても、前回学んだ WHERE や ORDER BY をそのまま使うことができます。巨大な1つの仮想テーブルが作られたと考えれば、これまでの知識がすべて活かせます。
7. AIを活用して「複雑なER図」からJOIN文を生成するプロンプト
4つ、5つとテーブルが増えてくると、どのキーとどのキーをONで結べばいいのか、人間でも頭がこんがらがってきます。ここで、AI(GeminiやChatGPT)の強力なコード生成能力を頼りましょう。
7-1. AIへ丸投げするプロンプトの極意
AIにJOIN文を作らせる時は、「テーブルのCREATE文(DDL)」をそのまま渡してしまうのが最も正確で確実です。
「あなたは熟練のデータベースエンジニアです。 以下の3つのテーブル構造(DDL)を読み込み、私の要件を満たす最適なMySQLのSELECT文(JOINを含む)を作成してください。 【要件】 ・全ユーザーの名前を出力すること(注文履歴がないユーザーも含むこと) ・ユーザーごとの購入した商品名を出力すること ・エイリアスを用いて可読性の高いSQLにすること ・なぜINNER JOINではなくLEFT JOINを選んだのか、初心者にわかるように解説すること 【テーブル構造】 CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(50)); CREATE TABLE orders (id INT PRIMARY KEY, user_id INT, product_id INT); CREATE TABLE products (id INT PRIMARY KEY, product_name VARCHAR(50));」
このようにAIを活用することで、「構文エラー」に悩まされる時間をゼロにし、あなたは「ビジネスの要件定義」という上位の仕事に集中することができるようになります。
まとめ:点と点が線になり、巨大な情報網が完成する
第5回の講座、お疲れ様でした!
データベース設計において、テーブルを細かく分割(正規化)することの真の目的は、「データの矛盾をなくしつつ、必要な時にJOINで自由自在にくっつけるため」でした。今日JOINをマスターしたことで、あなたのデータベーススキルは点から線へ、そして立体的な面へと進化しました。
INNER JOINとLEFT JOINの使い分けは、実務のバグ(データが表示されない等)の原因ナンバーワンでもあります。「どちらを基準にするか」を常に意識してSQLを組み立てる癖をつけてください。
次回、第6回「集計とグループ化:COUNT/SUMとGROUP BYで統計を取る」では、「Aさんは何件記事を書いたか?」「今月の合計売上はいくらか?」といった、データ分析やダッシュボード作成に必須となる集計関数の魔法について解説します。ついにデータベースが「計算」を始めます。お楽しみに!
▼ 複雑なクエリを高速処理する環境を ▼
巨大なJOIN処理もサクサク動く
「AlmaLinux 9対応 高速VPS」
SQLスキルを武器にキャリアップ
「バックエンドエンジニア転職」

コメント