MySQL 入門

MySQL LEFT JOIN

LEFT JOIN(左外部結合)操作は、「左」のテーブル(FROM 句で最初に指定されたテーブル)からすべてのレコードを取得し、「右」のテーブル(LEFT JOIN の後に指定されたテーブル)から一致するレコードを取得するために使用されます。右側のテーブルに一致するレコードが見つからない場合、結果セット内の右側テーブルに由来するカラムには NULL 値が入ります。

このタイプの結合は、特定のテーブル(左テーブル)のすべてのデータを結果セットに確実に含めたい場合に極めて重要です。たとえ、関連するもう一方のテーブル(右テーブル)に対応するデータが存在しなくても、情報を欠落させることなく抽出できます。

1. LEFT JOINの理解

LEFT JOIN の主な目的は、LEFT JOIN キーワードの左側に位置するテーブルのすべてのレコードを保持することです。2つのテーブル間の関連するカラムに基づいて行を組み合わせます。左テーブルのある行が右テーブルに一致する行を持たない場合、LEFT JOIN は結果にその左テーブルの行を含めますが、通常は右テーブルから取得されるはずのカラムをすべて NULL で埋めます。

ここで、「Customers(顧客)」と「Orders(注文)」という2つのテーブルを考えてみましょう。一人の顧客は複数の注文を出すこともあれば、まだ一度も注文を出していないこともあります。すべての顧客リストと、彼らが出した可能性のある注文を併せて確認したい場合、LEFT JOIN が最適な選択肢となります。これに対して、INNER JOIN(前節で説明)を使用した場合は、注文を出したことのある顧客のみが表示され、注文がない顧客は結果から除外されてしまいます。

LEFT JOIN の基本構文は以下の通りです:

SELECT
    column1,
    column2,
    ...
FROM
    TableA AS A
LEFT JOIN
    TableB AS B ON A.common_column = B.common_column;

ここでは、TableA が「左」テーブル、TableB が「右」テーブルとなります。TableA に存在するすべての行が結果に含まれます。

1.1 概念的な例:部門と従業員

「Departments(部門)」と「Employees(従業員)」という2つのテーブルを含む企業データベースを想像してください。各従業員は特定の部門に所属していますが、現在一人も従業員が配属されていない部門が存在する可能性もあります。

すべての部門をリストアップし、各部門についてそこで働いている従業員を表示したい場合は、LEFT JOIN を使用します。このとき、Departments テーブルを「左」テーブルに指定します。

SELECT
    d.department_name,
    e.employee_name
FROM
    Departments AS d
LEFT JOIN
    Employees AS e ON d.department_id = e.department_id;

このクエリは、Departments テーブルにあるすべての department_name を返します。ある部門に従業員がいる場合、その employee_name が部門名の横に表示されます。もし部門に従業員が一人もいない場合でも、その department_name は表示されますが、対応する employee_name カラムは NULL と表示されます。

2. LEFT JOINの実践的な例

デモンストレーションとして、world データベースのテーブルを使用します。具体的には、City(都市)テーブルと Country(国家)テーブルを考えます。すべての都市は一つの国に属していますが、Country テーブルにあるすべての国が City テーブルに都市の記録を持っているわけではありません(例えば、主要都市のみを保存している場合など)。

2.1 例1:すべての国とその首都をリストアップ

各国家とその首都をリストアップしたいとします。Country テーブル内のすべての国が、City テーブルに首都の対応するエントリを持っているとは限りません(データ欠落や未指定の場合)。LEFT JOIN を使用することで、すべての国が確実にリストに含まれるようになります。

Country テーブルには、首都である都市の ID を格納する Capital カラムがあります。

SELECT
    c.Name AS CountryName,
    cy.Name AS CapitalCity
FROM
    Country AS c
LEFT JOIN
    City AS cy ON c.Capital = cy.ID;

このクエリのポイント:

  • Country は左テーブル(エイリアス c)です。すべての国が含まれます。
  • City は右テーブル(エイリアス cy)です。
  • 結合条件 c.Capital = cy.ID は、Country テーブルの首都 ID を City テーブルの都市 ID と照合することで、国家とその首都を紐付けます。
  • ある国の Capital カラムに対応する ID が City テーブルに存在しない場合、その国の cy.Name は NULL になりますが、国自体はリストに表示されます。

2.2 例2:Cityテーブルに都市が存在しない国を検索

LEFT JOIN の一般的なユースケースの一つに、「左テーブルには存在するが、右テーブルに一致するものがないレコード」を探すというものがあります。これは、LEFT JOIN と、右テーブルの特定のカラムが NULL であるかをチェックする WHERE 句を組み合わせることで実現できます。

City テーブルに都市の記録が一つも存在しないすべての国を探してみましょう。

SELECT
    co.Name AS CountryName
FROM
    Country AS co
LEFT JOIN
    City AS ci ON co.Code = ci.CountryCode
WHERE
    ci.ID IS NULL;

このクエリの解説:

  • Country は左テーブル (co) です。
  • City は右テーブル (ci) です。
  • 結合条件によって各国家とその都市をリンクさせようとします。
  • WHERE ci.ID IS NULL 句によって、City テーブル内に一致するデータが見つからなかった行のみをフィルタリングします。City 由来の ID カラムが NULL であることは、その国に記録された都市が存在しないことを示しています。

2.3 例3:製品とそのカテゴリ(未分類製品を含む)を表示

Eコマースのシナリオを考えます。「Products(製品)」テーブルと「Categories(カテゴリ)」テーブルがあります。一部の製品には、まだカテゴリが割り当てられていない可能性があります。

カテゴリ未設定の製品も含め、すべての製品とその関連カテゴリをリストアップするには以下のように記述します:

SELECT
    p.product_name,
    c.category_name
FROM
    Products AS p
LEFT JOIN
    Categories AS c ON p.category_id = c.category_id;

このクエリはすべての product_name を返します。product_id が一致すれば category_name が表示されます。製品のカテゴリ ID が空であるか、カテゴリテーブルに一致するものがない場合、その製品の category_name は NULL になります。

3. まとめ

LEFT JOIN は、複数のテーブルからデータを組み合わせる際、左テーブルの全レコードを確実に結果セットに含めるための強力なツールです。総合的なレポートの作成(すべての主要エンティティと、それに関連する補助的なデータを表示する必要がある場合)や、関連テーブルに対応するエントリがない孤立したレコードの特定に非常に役立ちます。

LEFT JOIN をマスターすることで、より柔軟で完全なデータ取得が可能になります。一致するデータが存在することを保証できない場面で INNER JOIN を使用してしまうことによるデータの欠落を防ぐことができます。次回のレッスンでは、LEFT JOIN と対称的な機能を持ち、右テーブルの全レコードを含めることを保証する RIGHT JOIN について探求します。