現代のシステム開発やデータ分析において、リレーショナルデータベース(RDB)から必要な情報を効率的に取り出すスキルは不可欠です。
データベースは、データの重複を避けて整合性を保つために、情報を複数のテーブルに分割して保存する「正規化」という手法をとっています。
そのため、実際の業務で役立つ情報を得るには、バラバラに保存されたデータを「結合」して取得する操作が必要になります。
本記事では、SQLで複数テーブルからデータを取得する基本から、実務で頻出する応用的な活用例までを詳しく解説します。
SQLで複数テーブルを結合する意義
なぜデータベースでは、一つの大きなテーブルにすべてのデータを詰め込まないのでしょうか。
それは、一つのテーブルにすべての情報を入れると、同じデータが何度も繰り返し登場し、データの修正が必要になった際に矛盾が生じやすくなるからです。
例えば、ECサイトのシステムで「注文情報」と「顧客情報」を一つのテーブルにまとめると、同じ顧客が注文するたびにその氏名や住所を繰り返し入力しなければなりません。
これを防ぐために、顧客情報は「顧客テーブル」に、注文の詳細は「注文テーブル」に分けて管理します。
データを活用する際には、これらのテーブルを関連付けて呼び出す必要があり、その際に使用されるのがJOIN(結合)という命令です。
もっとも基本となる「INNER JOIN(内部結合)」
複数テーブルの結合において、もっとも頻繁に利用されるのがINNER JOINです。
INNER JOINは、結合する両方のテーブルに共通して存在するデータのみを取得する方法です。
まずは、以下の「users(ユーザー)」テーブルと「orders(注文)」テーブルを例に考えてみましょう。
| user_id | user_name |
|---|---|
| 1 | 田中太郎 |
| 2 | 鈴木一郎 |
| order_id | user_id | amount |
|---|---|---|
| 101 | 1 | 5000 |
| 102 | 1 | 3000 |
| 103 | 3 | 1500 |
INNER JOINの構文と動作
以下のクエリは、user_idをキーにして、注文を行ったユーザーの名前と注文金額を取得します。
-- ユーザーテーブルと注文テーブルを内部結合して取得
SELECT
u.user_id,
u.user_name,
o.order_id,
o.amount
FROM
users AS u
INNER JOIN
orders AS o
ON
u.user_id = o.user_id;
user_id | user_name | order_id | amount
--------+-----------+----------+--------
1 | 田中太郎 | 101 | 5000
1 | 田中太郎 | 102 | 3000
実行結果を見ると、user_idが「1」の田中太郎さんのデータのみが表示されています。
注文テーブルにあるuser_idが「3」のデータは、ユーザーテーブルに存在しないため除外されます。
このように、両方のテーブルに合致するレコードがある場合のみ結果に含まれるのがINNER JOINの特徴です。
データの漏れを防ぐ「LEFT JOIN(左外部結合)」
実務では「注文をしていないユーザーも含めて、全員の一覧を出したい」という場面が多々あります。
このような場合には、LEFT JOIN(左外部結合)を使用します。
LEFT JOINは、左側に記述したテーブル(FROM句のテーブル)を基準とし、右側のテーブルに一致するデータがなくても結果を返します。
LEFT JOINの実践例
先ほどのテーブルを使用して、すべてのユーザーと、もしあればその注文情報を取得してみましょう。
-- 注文の有無に関わらず、すべてのユーザーを取得
SELECT
u.user_id,
u.user_name,
o.order_id,
o.amount
FROM
users AS u
LEFT JOIN
orders AS o
ON
u.user_id = o.user_id;
user_id | user_name | order_id | amount
--------+-----------+----------+--------
1 | 田中太郎 | 101 | 5000
1 | 田中太郎 | 102 | 3000
2 | 鈴木一郎 | NULL | NULL
鈴木一郎さんは一度も注文をしていませんが、結果に含まれています。
このとき、対応するデータがない注文情報の列にはNULLが入ります。
「マスターテーブルにある情報はすべて表示したい」という状況では、このLEFT JOINがもっとも適しています。
3つ以上のテーブルを結合する方法
実際のシステムでは、3つ、4つと多くのテーブルを一度に結合することも珍しくありません。
例えば、「注文」から「商品名」と「注文したユーザー名」を同時に知りたい場合は、3つのテーブルを繋ぐ必要があります。
複数テーブル結合の記述ルール
結合を増やす場合は、単純にJOIN句を並べて記述していきます。
-- ユーザー、注文、注文明細の3つを結合
SELECT
u.user_name,
o.order_date,
p.product_name
FROM
users AS u
INNER JOIN
orders AS o ON u.user_id = o.user_id
INNER JOIN
order_details AS od ON o.order_id = od.order_id
INNER JOIN
products AS p ON od.product_id = p.product_id;
このように記述することで、複雑なデータ構造から必要な情報を一つの表として取り出すことができます。
結合する順番は、一般的にデータ量の少ないテーブルから結合するか、主軸となるトランザクションデータから結合を開始するのが基本です。
特殊な結合:SELF JOINとCROSS JOIN
日常的に使うことは少ないかもしれませんが、特定のケースで非常に強力なのが「自己結合(SELF JOIN)」と「クロス結合(CROSS JOIN)」です。
自己結合(SELF JOIN)
自己結合とは、同一のテーブルをあたかも別々のテーブルのように扱って結合する方法です。
組織図のように、社員データの中に「上司の社員ID」が含まれているような階層構造を扱う際によく使われます。
-- 社員テーブル(employees)を自分自身と結合
SELECT
e.employee_name AS 部下,
m.employee_name AS 上司
FROM
employees AS e
LEFT JOIN
employees AS m ON e.manager_id = m.employee_id;
同じテーブルを結合するため、エイリアス(AS)を使って別名を付けることが必須となります。
クロス結合(CROSS JOIN)
CROSS JOINは、2つのテーブルのすべての組み合わせ(直積)を生成します。
例えば、「すべての色」と「すべてのサイズ」の組み合わせリストを作りたい場合などに役立ちます。
結合条件(ON)を指定しないため、膨大なデータ量になる可能性があり、使用には注意が必要です。
複数テーブルから取得する際のパフォーマンス最適化
結合するテーブルが増えるほど、データベースにかかる負荷は増大します。
ここでは、効率的にデータを取得するための「3つの鉄則」を紹介します。
1. 適切なインデックスの付与
結合に使用するキー(ON句に記述する列)には、必ずインデックスを貼るようにしましょう。
インデックスがない状態で巨大なテーブル同士を結合すると、処理に数分以上かかってしまうこともあります。
2. SELECT * を避ける
実務のクエリでは、SELECT * を使用せず、必要なカラムだけを明示することが推奨されます。
不要なデータ転送を減らすことで、ネットワークの帯域消費を抑え、レスポンス速度を向上させることができます。
3. WHERE句による事前絞り込み
可能な限り、結合する前にデータを絞り込むことを意識してください。
結合した後に巨大な結果セットに対してフィルタをかけるよりも、小さなデータセット同士を結合する方が高速です。
SQLでの複数テーブル操作を支える「AS」と「USING」
クエリの可読性を高めるために、テーブルやカラムに別名を付けるAS句は積極的に活用しましょう。
特に複数のテーブルに同じ名前のカラム(例:created_atなど)が存在する場合、どちらのテーブルの列かを明示しないとエラーになります。
-- テーブル名に別名を付け、カラムを特定する
SELECT
usr.user_name,
ord.order_id
FROM
users AS usr
JOIN
orders AS ord ON usr.user_id = ord.user_id;
また、結合キーの名前が完全に一致している場合は、ONの代わりにUSINGを使うこともできます。
USING(user_id)のように記述することで、コードをよりシンプルに保つことが可能です。
実戦で役立つ「結合と集約」の組み合わせ
単にデータを結合するだけでなく、結合した結果を集計するケースも非常に多いです。
例えば、「各顧客ごとの合計注文金額」を算出する場合は、JOINとGROUP BYを組み合わせて使用します。
-- 顧客ごとの売上合計を算出
SELECT
u.user_name,
SUM(o.amount) AS total_sales
FROM
users AS u
INNER JOIN
orders AS o ON u.user_id = o.user_id
GROUP BY
u.user_id, u.user_name;
このように、複数テーブルの結合は、データ分析における「下準備」としての役割も担っています。
まとめ
SQLにおける複数テーブルからのデータ取得は、リレーショナルデータベースを活用する上でもっとも核心となる技術です。
まずは基本となるINNER JOINで一致するデータのみを取得することから始めましょう。
その後、データの取りこぼしを防ぐLEFT JOINや、複雑な構造に対応する複数テーブル結合へとステップアップしてください。
結合条件を正しく理解し、適切なインデックスを設計することで、膨大なデータから一瞬で必要な答えを導き出すことが可能になります。
日々の業務でクエリを書く際は、結果の正確さだけでなく、パフォーマンスも意識した美しいSQLを目指してみてください。
