SQL サブクエリのチュートリアル: 型、相関サブクエリ、および EXISTS と例
サブクエリは、別のクエリ内のクエリです。それぞれのタイプとそれがどこに当てはまるかを学び、相関サブクエリを適切に理解し、黙って行をまったく返さない NOT IN トラップを避けてください。
- サブクエリは、別のクエリ内にネストされたクエリであり、外側のクエリはその結果を使用します。
- サブクエリは
WHERE、FROM、SELECT、およびHAVINGに出現できます。 - 相関サブクエリは外側の行を参照するため、行ごとに論理的に評価されます。
- NULL が可能な場合は、
NOT EXISTSからNOT INを優先します。
質問によっては、回答するまでに 2 つのステップが必要になる場合があります。 「平均を上回る注文はどれですか?」まず平均を見つけて、それから各注文をそれと比較する必要があります。 SQL サブクエリ を使用すると、両方のステップを 1 つのステートメントとして記述できます。
このチュートリアルでは、2 つの小さな表を使用して各タイプを説明します。これらのクエリを SQLite 3.50 で実行し、表示された結果はその実行から得られたものです。標準のサブクエリ構文は広くサポートされており、データベースが異なる箇所を指摘します。
サブクエリとは何ですか?
サブクエリは、内部クエリまたはネスト クエリとも呼ばれ、別のステートメント内に丸括弧で囲まれた SELECT ステートメントです。概念的には、内部クエリは外部クエリで使用される結果を生成します。
SELECT id, total
FROM orders
WHERE total > (SELECT AVG(total) FROM orders);
サンプルテーブル
CREATE TABLE customers (id INT, name TEXT, city TEXT);
CREATE TABLE orders (id INT, customer_id INT, total INT);
INSERT INTO customers VALUES
(1,'Asha','Pune'), (2,'Ben','Delhi'), (3,'Chitra','Pune'), (4,'Dev','Goa');
INSERT INTO orders VALUES
(101,1,1200), (102,1,800), (103,2,300), (104,3,2500), (105,3,100);
Dev には命令がないことに注意してください。この詳細は、以下のいくつかの例で重要になります。
サブクエリの種類の概要
| 種類 | 返品 | 典型的な場所 | 一般的な演算子 |
|---|---|---|---|
| スカラー | 1 つの値 | WHERE、SELECT |
=, >, < |
| 複数行 | 1 列、複数行 | どこで |
IN、ANY、ALL |
| テーブル(導出) | 多くの列と行 | から |
テーブルのように使う |
| 相関関係のある | 外側の列による | WHERE、SELECT |
任意、および EXISTS |
スカラーサブクエリ
スカラー サブクエリは値を 1 つだけ返すため、単一の値が意味をなす場合にはどこでも使用できます。これは、以前のクエリとその結果です。すべての注文の平均は 980 です。
SELECT id, total
FROM orders
WHERE total > (SELECT AVG(total) FROM orders);
-- 101 | 1200
-- 104 | 2500
WHERE では直接集計を使用できないため、WHERE total > AVG(total) を記述することはできません。サブクエリは値を個別に計算することでこれを回避します。
「スカラー」サブクエリが複数の行を返した場合はどうなるでしょうか? PostgreSQL、MySQL、SQL Server などのほとんどのデータベースではエラーが発生します。 SQLite はそうではありません。私たちのテストでは、最初の行のみがサイレントに使用されましたが、実際のバグが隠れている可能性があるため、この動作に依存しないでください。
入っている、入っていない
内部クエリがリストを返す場合は、IN を使用します。これにより、少なくとも 1 つの注文が 1000 を超える顧客が検索されます。
SELECT name FROM customers
WHERE id IN (SELECT customer_id FROM orders WHERE total > 1000);
-- Asha
-- Chitra
NOT IN はその逆で、注文したことがない顧客を見つけます。
SELECT name FROM customers
WHERE id NOT IN (SELECT customer_id FROM orders);
-- Dev
ここではこれで機能しますが、NOT IN には危険なエッジケースがあり、これについては以下で説明します。
FROM のサブクエリ
FROM のサブクエリは一時テーブルとして機能し、多くの場合、派生テーブルと呼ばれます。これは、各都市の顧客あたりの平均支出額を求める次のクエリのように、2 回集計する必要がある場合に便利です。
SELECT city, AVG(spent) AS avg_spent
FROM (
SELECT c.city, SUM(o.total) AS spent
FROM customers c
JOIN orders o ON o.customer_id = c.id
GROUP BY c.id, c.city
) AS per_customer
GROUP BY city;
-- Delhi | 300.0
-- Pune | 2300.0
内側のクエリは各顧客を合計し、外側のクエリはそれらの合計を平均します。一部のデータベースではエイリアスが必要なため、派生テーブルにエイリアスを付けることは良い習慣です。 GROUP BY が初めての場合は、SQL GROUP BY と HAVING を参照してください。
SELECT のサブクエリ
スカラー サブクエリでは計算列を追加することもできます。これは顧客ごとの注文をカウントします。
SELECT name,
(SELECT COUNT(*) FROM orders o WHERE o.customer_id = c.id) AS order_count
FROM customers c;
-- Asha | 2
-- Ben | 1
-- Chitra | 2
-- Dev | 0
Dev はゼロで表示されます。 Dev には一致する行がないため、内部結合では Dev が完全に削除されます。 LEFT JOIN と GROUP BY を使用しても同じ結果が得られます。これについては、SQL 結合の説明 で説明します。
相関サブクエリ
内部クエリが外部クエリの c.id を参照しているため、前の例も相関しています。非相関サブクエリは単独で実行できます。相関関係のあるものは、現在の外側の行の値に依存するため、できません。
これにより、同じ顧客自身の平均を上回る注文が見つかります。
SELECT o.id, o.customer_id, o.total
FROM orders o
WHERE o.total > (
SELECT AVG(i.total)
FROM orders i
WHERE i.customer_id = o.customer_id
);
-- 101 | 1 | 1200
-- 104 | 3 | 2500
読み方は次のとおりです。注文 101 の場合、内部クエリは顧客 1 の注文 (1200 と 800) を平均し、1000 となります。1200 の方が大きいため、行は保持されます。次数 102 については、同じ平均が適用されますが、800 はそれより大きくないため、削除されます。顧客 2 の 1 つの注文はそれ自体の平均に等しいため、これも削除されます。
| 特徴 | 相関関係がない | 相関関係のある |
|---|---|---|
| 外部クエリ値を使用しますか? | いいえ | はい |
| 単独で実行できますか? | はい | いいえ |
| 論理実行 | 一度 | 外側の行ごとに 1 回 |
| パフォーマンスのリスク | 通常は低い | 大きなテーブルでは高い位置に置くことができます |
「行ごとに 1 回」はロジックを記述しており、クエリ オプティマイザーはこれらをはるかに効率的なものに書き換えることがよくあります。それでも、遅い相関サブクエリは調査する価値があります。内側の列のインデックス (ここでは orders.customer_id) は、大きな違いを生む可能性があります。 SQL インデックスの説明を参照してください。
存在するものと存在しないもの
EXISTS は、はいまたはいいえで答える質問をします。内部クエリは少なくとも 1 行を返しますか?最初の一致で停止する可能性があり、内部で何を選択するかは重要ではないため、SELECT 1 と書かれることがよくあります。
SELECT c.name FROM customers c
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.id);
-- Asha, Ben, Chitra
SELECT c.name FROM customers c
WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.id);
-- Dev
NOT IN NULL トラップ
これは、サブクエリの最も一般的なバグの 1 つです。 1 つの実際の ID と 1 つの NULL を持つテーブルを作成し、それに対して両方のフォームを実行しました。
CREATE TABLE r (customer_id INT);
INSERT INTO r VALUES (1), (NULL);
SELECT name FROM customers
WHERE id NOT IN (SELECT customer_id FROM r);
-- (no rows at all)
SELECT c.name FROM customers c
WHERE NOT EXISTS (SELECT 1 FROM r WHERE r.customer_id = c.id);
-- Ben, Chitra, Dev
なぜ最初のものは何も返さないのでしょうか? id NOT IN (1, NULL)はid <> 1 AND id <> NULLを意味します。 NULL との比較は真ではなく未知であるため、条件全体が真になることはあり得ません。エラー メッセージや警告はなく、ありそうな空の結果が表示されるだけです。
ヒント: デフォルトでは、アンチ結合には NOT EXISTS を使用します。 NOT IN を使用する場合は、サブクエリ内に WHERE customer_id IS NOT NULL を追加します。
サブクエリ vs JOIN vs CTE
多くのクエリは複数の方法で記述できます。先ほど、IN を使用して大量の注文がある顧客を見つけました。結合を使用した場合の同じ結果は次のとおりです。
SELECT DISTINCT c.name FROM customers c
JOIN orders o ON o.customer_id = c.id AND o.total > 1000;
-- Asha
-- Chitra
DISTINCTに注目してください。顧客に複数の一致がある場合、結合では重複が生成される可能性がありますが、IN と EXISTS では重複が生成されません。
| 使用する | いつ |
|---|---|
| 参加する | 両方のテーブルの列が必要です |
| 存在するか存在する | 一致するものが存在するかどうかによってフィルタリングするだけで済みます |
| 派生テーブル | 最初に集計する必要がありますが、短いままです |
| CTE | ロジックに複数のステップがあるか、複数回使用されています |
| 窓関数 | 各行の横にグループごとの図が必要です |
パフォーマンスはデータベースやデータによって異なり、最新のオプティマイザーは多くの場合、同様の計画を作成します。したがって、最初に最も明確なフォームを選択し、それが遅いことが判明した場合は EXPLAIN をチェックしてください。
よくある間違い
- 複数行の結果で
=を使用します。INを使用するか、内部クエリが 1 行を返すようにしてください。 - Null 許容列で
NOT INを使用すると、 警告なしにすべての行が失われる可能性があります。 - 不明瞭なエイリアスの使用。 相関クエリでは、すべての列を修飾しないと、間違ったテーブルにバインドされる可能性があります。
- 深いネスト。 3 つのレベルは読みにくいため、CTE に変換します。
- 内部に
ORDER BYを追加します。 通常、外部の結果には影響せず、一部のデータベースでは拒否されます。
まとめ
サブクエリは、2 段階の質問を表現する自然な方法です。スカラー サブクエリと IN から始めて、一度に 1 つの外側の行を推論して、相関サブクエリを練習します。 NULL が可能な場合は常に NOT EXISTS を思い出してください。練習するには、サブクエリ、結合、CTE を使用して 3 つの方法で 1 つの質問を作成し、結果と読みやすさの両方を比較します。
よくある質問
SQLのサブクエリとは何ですか?
サブクエリは、括弧内に別の SQL ステートメント内にネストされた SELECT ステートメントです。外側のステートメントは、その結果を値、リスト、またはテーブルとして使用します。
相関サブクエリとは何ですか?
外部クエリの列を参照するサブクエリです。これは現在の外側の行に依存するため、単独で実行することはできません。
サブクエリと JOIN はどちらが速いですか?
これはデータベースとデータに依存し、オプティマイザーは多くの場合、同様の計画を作成します。最初に最も明確なバージョンを作成し、パフォーマンスが重要な場合は実行計画を比較します。
IN と EXISTS の違いは何ですか?
IN は、値を結果のリストと比較します。 EXISTS は、行が返されたかどうかを確認します。 NULL を否定すると動作が異なります。
サブクエリは複数の列を返すことができますか?
はい、FROM で派生テーブルとして使用する場合、また一部のデータベースでは行の比較も可能です。スカラー サブクエリは 1 つの列と 1 つの行のみを返す必要があります。
学び続ける
テスト、WordPress、または SEO についてサポートが必要ですか?
私たちは、チームがより優れたソフトウェアを出荷し、サイトを成長させるのを支援します。あなたが取り組んでいることを教えてください。明確な次のステップを提案します。
詳しいガイドはこちら テスト、AI、最新の開発
この記事は気に入りましたか?トピックを選択して、最新のチュートリアルに直接アクセスしてください。
AI 幻覚の説明: 言語モデルが物事をでっち上げる理由とそれを軽減する方法
AI 幻覚について説明しました。幻覚とは何か、言語モデルが幻覚を生成する理由、一般的な種類、グラウンディングとプロンプトで幻覚を軽減する方法、そして私たちが実行した簡単なチェックです。
記事を読む →DevOps環境変数の説明: .env ファイル、シークレット、および設定が正しく行われている
実行した Python の例を使用して環境変数について説明します。環境変数の読み取りと設定、すべての値が文字列である理由、.env ファイル、プロセスの継承、シークレット、検証について説明します。
記事を読む →ソフトウェア開発API レート制限の説明: アルゴリズム、ヘッダー、および 429 エラーの処理
API レート制限については、実行したシミュレーションで説明します。トークン バケット、固定ウィンドウ、スライディング ウィンドウ、429 応答、レート制限ヘッダー、バックオフによる再試行、およびテスト制限です。
記事を読む →テスト自動化Python でのモッキング:unittest.mock、patch、およびテストを嘘にする間違い
Python でのunittest.mockによるモック、テスト済み: モックとreturn_value、side_effect、名前が使用されている場所のパッチ、仕様と自動仕様、日付のモック、およびよくある間違い。
記事を読む →ソフトウェアテストコードカバレッジの説明: 回線カバレッジとブランチカバレッジ、および 100% の実際の意味
実際のcoverage.pyの実行でコードカバレッジを説明: 行、分岐、条件のカバレッジ、レポートの読み取り、100%のカバレッジでもバグを見逃す可能性がある理由、および賢明なターゲット。
記事を読む →データベースとSQLSQL における DELETE、TRUNCATE、DROP: 違い、ロールバック、およびそれぞれをいつ使用するか
DELETE、TRUNCATE、DROP について、テスト済み SQL を使用して説明します。それぞれの削除内容、ロールバック、トリガー、ID リセット、速度、PostgreSQL、MySQL、SQL Server、SQLite の違いについて説明します。
記事を読む →データベースとSQLSQL ビューの説明: 作成、使用、落とし穴の回避、例を示します
SQL ビューについて、実行したクエリとともに説明しました。ビューの作成、ライブ結果、列の非表示、パフォーマンス、更新可能なビューと具体化されたビュー、壊れた依存関係、ベスト プラクティスについて説明します。
記事を読む →ソフトウェア開発TypeScript インターフェイスと型: 実際の違い、コンパイラでテスト
TypeScript インターフェイスと型、実際のコンパイラでチェック: 両方でできること、片方だけでできること、宣言のマージ、拡張と交差、および従うべき簡単なルール。
記事を読む →ソフトウェア開発Python *args と **kwargs の説明: 柔軟な関数と明確な例
Python の *args と **kwargs について、実行したコードで説明しました。パッキングとアンパッキング、パラメーターの順序、キーワードのみの引数と位置のみの引数、デコレーター、一般的なエラーです。
記事を読む →ソフトウェア開発Python Lambda 関数: 構文、実際の用途、および使用すべきでない場合
Python のラムダ関数について、実行したコードとともに説明します。構文、キーによるソート、マップとフィルター、クロージャー、遅延バインディング トラップ、通常の def がより適切な選択である場合。
記事を読む →人工知能AIエージェントとは何ですか?シンプルなエージェント ループを実行できる仕組み
AIエージェントとは何ですか?エージェント ループ、ツール、メモリ、ガードレール、実行した小さな Python ループ、実際のリスク、およびシンプルなチャットボットがより良い選択である場合についてのわかりやすいガイドです。
記事を読む →DevOpsモニタリングと可観測性: ログ、メトリクス、トレースによる本当の違い
モニタリングと可観測性について説明: 既知および未知の障害、ログ、メトリクスとトレース、チェックアウト インシデントのウォークスルー、ゴールデン シグナル、OpenTelemetry、および便利なアラート。
記事を読む →DevOpsDocker ボリュームとバインド マウント: コンテナ データを正しい方法で保持する
Docker ボリュームとバインド マウントについて説明します。コンテナ データが消える理由、名前付きボリューム、バインド マウント、tmpfs、Compose の例、バックアップ、アクセス許可、およびそれぞれをいつ使用するかについて説明します。
記事を読む →テスト自動化TDD と BDD: 主な違い、1 つの機能が両方の方法で構築され、それぞれをいつ使用するか
TDD と BDD は、両方の方法で構築された 1 つの割引機能で説明されています。実行したコードによるレッド-グリーン-リファクタリング、Given-When-Then シナリオ、ツール、トレードオフ、およびそれぞれをいつ使用するかです。
記事を読む →ソフトウェアテストユーザー受け入れテスト (UAT): プロセス、チェックリスト、および実際の例
ユーザー受け入れテストの説明: 誰が実行するか、QA との違い、7 段階のプロセス、UAT テスト ケース テンプレート、チェックリスト、サインオフ基準、およびよくある間違い。
記事を読む →データベースとSQLSQL NULL 値の説明: IS NULL、COALESCE、NULLIF、および回避すべきトラップ
SQL の NULL 値について、実行したクエリで説明しました。3 つの値のロジック、IS NULL、COALESCE、NULLIF、集計、結合、並べ替えでの NULL の扱い方、およびそれが密かに引き起こすバグです。
記事を読む →データベースとSQLSQL CASE WHEN ステートメント: 構文、実際の例、およびよくある間違い
SQL CASE WHEN では、単純な検索フォーム、条件順序、SUM を使用したピボット、COUNT トリック、カスタム並べ替え、更新、回避すべき NULL トラップなど、実行したクエリを使用して説明しました。
記事を読む →ソフトウェア開発JWTとは何ですか? JSON Web トークンを実際の例で説明する
JWTとは何ですか? JSON Web トークンについて説明しました。3 つの部分、実行したコード内で構築および改ざんされたトークン、JWT とセッション、ストレージ、リフレッシュ トークン、およびセキュリティ上の間違いです。
記事を読む →ソフトウェア開発JavaScript イベントループの説明: コールスタック、マイクロタスク、タイマーと例
JavaScript イベント ループについて、実行したコードで説明しました。コール スタック、タスク キュー、マイクロタスク キュー、setTimeout 0 が即時ではない理由、ブロッキング、スターベーション、およびインタビュー クイズです。
記事を読む →ソフトウェア開発Python ジェネレーターと収量の説明: 実際の例でメモリを節約する
Python ジェネレーターと Yield について、実行したコードを使用して説明します。一時停止と再開の方法、メモリ節約の測定、パイプライン、無限シーケンス、Yield from、send、および一般的なトラップについて説明します。
記事を読む →人工知能埋め込みとは何ですか?ベクトルとコサインの類似度を簡単に説明
埋め込みとは何ですか?ベクトル、コサイン類似度、セマンティック検索に関する平易なガイド。私たちが実行した小さな例、一般的な使用法、モデルの選択、既知の制限が含まれています。
記事を読む →DevOpsBlue-Green デプロイメントと Canary デプロイメント: 違い、トレードオフ、および選択方法
ブルー グリーン デプロイメントとカナリア デプロイメントについて説明: それぞれの仕組み、コスト、リスクとロールバック、テストした安定したユーザー バケット化、データベース移行、ローリング アップデート、および選択方法。
記事を読む →テスト自動化不安定なテスト: 問題の原因とそれを永久に修正する方法
不安定なテストについては、私たちが実行したデモを使用して説明しました。主な原因、確認および診断する方法、タイミングの適切な修正、共有された状態と日付、隔離プロセスなどです。
記事を読む →ソフトウェアテスト負荷テストとストレス テスト: 違い、メトリクス、および k6 の例
ロード テストとストレス テストについて説明します。それぞれが何を証明するか、スパイク テストとソーク テスト、重要な指標、平均値が誤解を招く理由、K6 の例、テストの計画方法について説明します。
記事を読む →ソフトウェアテストテスト ケースとテスト シナリオ: 主な違い、例、およびそれぞれをいつ使用するか
テスト ケースとテスト シナリオの説明: 明確な定義、並べて表、シナリオからケースへの 1 つのパスワード リセット機能、テンプレート、およびそれぞれをいつ使用するか。
記事を読む →データベースとSQLSQL クエリの最適化: 実行プランを読み取り、タイミングを考慮して遅いクエリを修正
SQL クエリの最適化は 100 万行でテストされています。実行プランの読み取り、適切なインデックスの追加、検索対象外のフィルターの修正、複合インデックスとキーセットのページネーションの使用。
記事を読む →データベースとSQLSQL UNION と UNION ALL: 違い、例、テストされたパフォーマンス
SQL UNION と UNION ALL について、実行したクエリで説明しました。重複、両方に従うルール、実際のデータのバグ、測定されたパフォーマンス、INTERSECT と EXCEPT、およびそれぞれをいつ使用するかについて説明します。
記事を読む →ソフトウェア開発JavaScript Fetch API チュートリアル: GET、POST、エラー処理、タイムアウト
ローカル サーバーに対してテストされた JavaScript Fetch API チュートリアル: GET と POST、404 がスローされない理由、再利用可能なヘルパー、クエリ文字列、タイムアウト、CORS、およびよくある間違い。
記事を読む →ソフトウェア開発Python ファイル処理: 読み取り、書き込み、CSV、JSON、パスライブラリと例
Python のファイル処理について、実行したコードで説明します。オープン モード、大きいファイルの読み取り、CSV、JSON、パスライブラリ、バイナリ ファイル、安全な書き込み、エラー処理、およびよくある間違いについて説明します。
記事を読む →ソフトウェア開発Python のクラスとオブジェクト: 実際の例を含む OOP チュートリアル
Python のクラスとオブジェクトについて、実行したコードとともに説明します: __init__、メソッド、クラスとインスタンスの属性、継承、プロパティ、ダンダー メソッド、データクラス、およびよくある間違い。
記事を読む →人工知能大規模言語モデルとは何ですか? LLM の仕組みを誇大広告なしで説明
大規模言語モデルとは何ですか? LLM は、トークン、エンベディング、アテンション、トレーニング、温度およびコンテキスト ウィンドウ、小さな次の単語のデモ、実際の制限など、平易な言葉で説明されています。
記事を読む →DevOps初心者のための Linux コマンド: 例を含む基本ターミナル ガイド
初心者向けの Linux コマンド。ナビゲーション、ファイル、ログの表示、grep、パイプ、権限、プロセス、アーカイブなどのタスク別にグループ化されており、実用的なログ分析の例も含まれています。
記事を読む →テスト自動化Selenium と Playwright: 主な違い、コードの比較、どちらを選択するか
Selenium と Playwright の比較: アーキテクチャ、待機、ブラウザと言語のサポート、デバッグ、両方のツールでの同じテスト、移行のアドバイスと選択方法。
記事を読む →ソフトウェアテスト等価性分割: テスト設計手法を実際の例で説明
等価性パーティション化について説明しました。有効なパーティションと無効なパーティションを見つける方法、年齢、パスワード、クーポンに関する実際の例、実行したテスト、境界と組み合わせる理由。
記事を読む →ソフトウェアテストソフトウェアテストにおける重大度と優先度: 相違点、マトリックス、および実際の例
ソフトウェア テストにおける重大度と優先度について説明します。明確な定義、例を含む 4 方向のマトリックス、レベル スケール、それぞれの設定者、バグ トリアージの実行方法。
記事を読む →データベースとSQLSQL サブクエリのチュートリアル: 型、相関サブクエリ、および EXISTS と例
実行したクエリを含む SQL サブクエリのチュートリアル: スカラー、IN、FROM、SELECT サブクエリ、相関サブクエリ、EXISTS、NOT IN NULL トラップ、サブクエリ対 JOIN 対 CTE。
記事を読む →データベースとSQLSQL トランザクションと ACID の説明: COMMIT、ROLLBACK、分離レベル
SQL トランザクションと ACID について、実行した銀行振込で説明しました。COMMIT、ROLLBACK、セーブポイント、ロック、分離レベル、ダーティ リード、デッドロック、ベスト プラクティスについて説明します。
記事を読む →ソフトウェア開発Git mergeとrebaseの違い:例と使い分け
Git mergeとrebaseの違いを、実際のログ出力、比較表、rebaseの黄金律、安全なチームワークフローで解説します。
記事を読む →ソフトウェア開発Pythonのリスト内包表記:構文・例・使うべきでない場面
Pythonのリスト内包表記を解説:構文、フィルタ、if-else、ネストしたループ、実測した処理時間、そして通常のforループの方が良い場面。
記事を読む →データベースとSQLSQL JOINを例で解説(Inner・Left・Right・Full)
サンプルのテーブルとクエリでSQL JOINを順に解説。INNER、LEFT、RIGHT、FULL、CROSS、SELFに加え、よくある間違いとパフォーマンスのコツも紹介します。
記事を読む →