【徹底解説】LEFT JOINとWHERE句を使って、あるテーブルに存在するレコードのうち、別のテーブルに存在しないレコードを選択する方法

2024-04-02

SQLでテーブル間で存在しないレコードを選択する方法

このチュートリアルでは、SQL Server、MySQL、PostgreSQLなどのデータベースで、あるテーブルに存在するレコードのうち、別のテーブルに存在しないレコードを選択する方法を解説します。

問題

customers テーブルと orders テーブルがあるとします。customers テーブルには顧客情報、orders テーブルには注文情報が格納されています。

このとき、customers テーブルに存在する顧客のうち、orders テーブルに注文履歴がない顧客 をすべて選択したい場合があります。

解決方法

この問題を解決するには、LEFT JOINWHERE 句を使用します。

SELECT c.*
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id
WHERE o.customer_id IS NULL;

このクエリは、以下の処理を行います。

  1. customers テーブルと orders テーブルを c.ido.customer_id で結合します。
  2. orders テーブルの customer_idNULL であるレコードのみを選択します。

解説

  • LEFT JOIN は、左側のテーブルのすべてのレコードを返し、右側のテーブルと一致するレコードがあればそれを追加します。
  • WHERE 句は、条件に一致するレコードのみを抽出します。
  • o.customer_id IS NULL は、orders テーブルに customer_id が存在しないことを意味します。

EXISTS キーワードを使用する方法もあります。

SELECT c.*
FROM customers c
WHERE EXISTS (
  SELECT 1
  FROM orders o
  WHERE o.customer_id = c.id
);

LEFT JOINWHERE 句、または EXISTS キーワードを使用することで、あるテーブルに存在するレコードのうち、別のテーブルに存在しないレコードを選択することができます。




-- テーブル定義
CREATE TABLE customers (
  id INT PRIMARY KEY,
  name VARCHAR(255)
);

CREATE TABLE orders (
  id INT PRIMARY KEY,
  customer_id INT,
  order_date DATETIME
);

-- データ挿入
INSERT INTO customers (id, name) VALUES (1, 'John Doe');
INSERT INTO customers (id, name) VALUES (2, 'Jane Doe');
INSERT INTO customers (id, name) VALUES (3, 'Peter Smith');

INSERT INTO orders (id, customer_id, order_date) VALUES (1, 1, '2023-01-01');
INSERT INTO orders (id, customer_id, order_date) VALUES (2, 2, '2023-02-01');

-- クエリ実行
SELECT c.*
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id
WHERE o.customer_id IS NULL;

このクエリを実行すると、以下の結果が返されます。

id | name
------- | --------
3 | Peter Smith
SELECT c.*
FROM customers c
WHERE EXISTS (
  SELECT 1
  FROM orders o
  WHERE o.customer_id = c.id
);

このクエリは、customers テーブルの各レコードについて、orders テーブルに customer_id が存在するかどうかを確認します。orders テーブルに customer_id が存在しないレコードのみが返されます。




他の方法

NOT IN 句を使用すると、あるテーブルの列の値が別のテーブルの列の値のリストに存在しないレコードを選択できます。

SELECT c.*
FROM customers c
WHERE c.id NOT IN (
  SELECT customer_id
  FROM orders
);

このクエリは、customers テーブルの id 列の値が orders テーブルの customer_id 列の値のリストに存在しないレコードをすべて選択します。

SELECT c.*
FROM customers c
FULL OUTER JOIN orders o ON c.id = o.customer_id
WHERE o.customer_id IS NULL OR c.id IS NULL;

このクエリは、customers テーブルと orders テーブルを c.ido.customer_id で結合し、orders テーブルの customer_idNULL または customers テーブルの idNULL であるレコードをすべて返します。

サブクエリを使用して、別のテーブルに存在しないレコードを選択することもできます。

SELECT c.*
FROM customers c
WHERE c.id IN (
  SELECT customer_id
  FROM (
    SELECT customer_id
    FROM orders
    GROUP BY customer_id
    HAVING COUNT(*) = 0
  ) AS t
);

このクエリは、orders テーブルに注文履歴が1件もない顧客の customer_id のリストを取得し、そのリストに含まれる customer_id を持つ customers テーブルのレコードをすべて選択します。

上記の方法以外にも、テーブル間で存在しないレコードを選択する方法はいくつかあります。どの方法を使用するかは、状況によって異なります。


sql sql-server t-sql


Python、Ruby、JavaScriptでできる!CSVファイルからINSERT SQLステートメントを生成する方法

CSVファイルからINSERT SQLステートメントを生成するには、次の手順が必要です。CSVファイルの構造を理解する CSVファイルには、ヘッダー行があるかどうかを確認します。 各列のデータ型を確認します。CSVファイルの構造を理解する...


SQL Server、MySQL、PostgreSQL、Oracleでn番目に大きい値を取得する

MAX()関数とサブクエリこの方法は、まずMAX()関数を使って最大値を取得し、その値をサブクエリで除外することで、n番目に大きい値を取得します。例:このクエリは、studentsテーブルのscore列の2番目に大きい値を取得します。ROW_NUMBER()関数は、各行に順位を割り当てる関数です。この関数を使って、n番目に大きい値を取得することができます。...


SQLiteクエリで単一引用符をエスケープする方法

単一引用符は、SQLiteクエリにおいて以下の2つの意味を持ちます。文字列リテラルの開始と終了クエリ内の識別子を囲む例えば、以下のクエリは、name列が'John'という値を持つレコードをすべて選択します。この場合、'John'は単一引用符で囲まれており、文字列リテラルであることを示しています。...


カスタム ORDER BY を駆使して SQLite でデータを自在にソート

概要SQLite は、軽量で使いやすいデータベース管理システム (DBMS) です。多くのアプリケーションでデータの保存に使用されています。SQLite は、ORDER BY 句を使用してデータのソートをサポートしています。しかし、デフォルトの ORDER BY 句は、列の値に基づいた単純なソートしか行えません。...


wait_timeout設定でMariaDBサーバーのタイムアウト時間を調整する方法

MariaDBサーバーで、クライアント接続が600秒後にタイムアウトしてしまう問題が発生しているとのことですね。これは、クライアントが600秒間サーバーとやり取りを行わない場合、自動的に接続が切断されてしまうという問題です。影響この問題は、以下の様な影響を及ぼす可能性があります。...