SQL EXISTSステートメントはどのように機能しますか?


88

SQLを学習しようとしていますが、EXISTSステートメントを理解するのに苦労しています。私は「存在する」についてのこの引用に出くわしました、そして、何かを理解しません:

存在演算子を使用すると、サブクエリは0、1、または多数の行を返すことができ、条件はサブクエリが行を返したかどうかをチェックするだけです。サブクエリのselect句を見ると、単一のリテラル(1)で構成されていることがわかります。含まれているクエリの条件は、返された行数を知るだけでよいため、サブクエリが返した実際のデータは関係ありません。

私が理解していないのは、外部クエリがサブクエリがチェックしている行をどのように知るのかということです。例えば:

SELECT *
  FROM suppliers
 WHERE EXISTS (select *
                 from orders
                where suppliers.supplier_id = orders.supplier_id);

サプライヤーと注文テーブルのIDが一致する場合、サブクエリはtrueを返し、サプライヤーのテーブルの一致する行のすべての列が出力されることを理解しています。私が得られないのは、trueまたはfalseのみが返される場合に、サブクエリがどの特定の行(たとえば、サプライヤID 25の行)を出力するかをどのように伝達するかです。

外部クエリとサブクエリの間に関係はないように見えます。

回答:


98

このように考えてください:

からの「各」行について、条件を満たすSuppliers「存在する」行がOrderテーブルにあるかどうかを確認しますSuppliers.supplier_id(これは現在の「行」の外部クエリから取得されます)= Orders.supplier_id。一致する最初の行を見つけたら、そこで停止します-はWHERE EXISTS満足しています。

外部クエリとサブクエリの間の魔法のリンクは、Supplier_id評価された各行の外部クエリからサブクエリに渡されるという事実にあります。

または、言い換えると、外部クエリのテーブル行ごとにサブクエリが実行されます。

サブクエリが全体として実行され、「true / false」を取得してから、この「true / false」条件を外部クエリと照合しようとするのとは異なります。


7
ありがとう!「これは、全体として実行されるサブクエリとは異なり、「true / false」を取得してから、この「true / false」条件をouterqueryと照合しようとします。」サブクエリがどのように機能するか(そして何度も実行されるか)、私はそれを本当にクリアしたものですが、サブクエリは外部クエリに依存しているため、行ごとに1回実行する必要があるため、あなたが言ったことは理にかなっています
Clarence Liu

32

外部クエリとサブクエリの間に関係はないように見えます。

EXISTSの例の中のWHERE句は何をしていると思いますか?SUPPLIERS参照がEXISTS句内のFROM句またはJOIN句にない場合、どのようにしてその結論に達しますか?

EXISTSはTRUE / FALSEを評価し、基準の最初の一致でTRUEとして終了します。これがIN。よりも高速になる可能性がある理由です。また、EXISTSのSELECT句は無視されることに注意してください-IE:

SELECT s.*
  FROM SUPPLIERS s
 WHERE EXISTS (SELECT 1/0
                 FROM ORDERS o
                WHERE o.supplier_id = s.supplier_id)

...ゼロ除算エラーが発生するはずですが、そうではありません。WHERE句は、EXISTS句の最も重要な部分です。

また、親に複数の子レコードが関連付けられている場合、親レコードが重複するため、JOINはEXISTSの直接の代替ではないことに注意してください。


1
私はまだ何かが欠けています。最初の一致で終了した場合、出力はどのようにしてo.supplierid = s.supplieridのすべての結果になりますか?代わりに最初の結果を出力するだけではないでしょうか。
ダン

3
@Dan:EXISTS終了し、最初の一致でTRUEを返します-サプライヤーがORDERSテーブルに少なくとも1回存在するためです。ORDERSに複数の子関係があるために、SUPPLIERデータの重複を確認したい場合は、JOINを使用する必要があります。しかし、ほとんどの場合、その重複は望ましくなく、GROUP BY / DISTINCTを実行すると、クエリにオーバーヘッドが追加される可能性があります。 SQL ServerEXISTSよりも効率的でSELECT DISTINCT ... FROM SUPPLIERS JOIN ORDERS ...、最近OracleやMySQLでテストされていません。
OMGポニー

質問がありました。外部クエリでSELECTEDされたすべてのレコードに対してマッチングが行われるのですか。サプライヤから5行が選択されている場合と同様に、注文から5回フェッチします。
Rahul Kadukar 2016年

24

あなたはいずれかを使用して同じ結果を生成することができJOINEXISTSIN、またはINTERSECT

SELECT s.supplier_id
FROM suppliers s
INNER JOIN (SELECT DISTINCT o.supplier_id FROM orders o) o
    ON o.supplier_id = s.supplier_id

SELECT s.supplier_id
FROM suppliers s
WHERE EXISTS (SELECT * FROM orders o WHERE o.supplier_id = s.supplier_id)

SELECT s.supplier_id 
FROM suppliers s 
WHERE s.supplier_id IN (SELECT o.supplier_id FROM orders o)

SELECT s.supplier_id
FROM suppliers s
INTERSECT
SELECT o.supplier_id
FROM orders o

1
偉大な答えが、また、それが使用することをお勧めではありませんが、相関関係を避けるために存在していることを気に
フロリアンフレーリッヒ

1
サプライヤに1,000万行、注文に1億行がある場合、どのクエリがより高速に実行されると思いますか。その理由は何ですか。
Teja 2017

7

次のようなwhere句がある場合:

WHERE id in (25,26,27) -- and so on

一部の行が返される理由と返されない理由を簡単に理解できます。

where句が次のような場合:

WHERE EXISTS (select * from orders where suppliers.supplier_id = orders.supplier_id);

つまり、同じIDのordersテーブルに既存のレコードがある行を返します。


2

これは非常に良い質問なので、このトピックに関する非常に詳細な記事をブログに書くことにしまし

データベーステーブルモデル

データベースに次の2つのテーブルがあり、1対多のテーブル関係を形成していると仮定します。

SQLEXISTSテーブル

studentテーブルには、親である、そしてstudent_gradeそれは学生のテーブルのid主キー列を参照STUDENT_ID外部キー列を持っているので、子テーブルです。

student tableは、次の2つのレコードが含まれています。

| id | first_name | last_name | admission_score |
|----|------------|-----------|-----------------|
| 1  | Alice      | Smith     | 8.95            |
| 2  | Bob        | Johnson   | 8.75            |

また、student_gradeテーブルには、学生が受け取った成績が保存されています。

| id | class_name | grade | student_id |
|----|------------|-------|------------|
| 1  | Math       | 10    | 1          |
| 2  | Math       | 9.5   | 1          |
| 3  | Math       | 9.75  | 1          |
| 4  | Science    | 9.5   | 1          |
| 5  | Science    | 9     | 1          |
| 6  | Science    | 9.25  | 1          |
| 7  | Math       | 8.5   | 2          |
| 8  | Math       | 9.5   | 2          |
| 9  | Math       | 9     | 2          |
| 10 | Science    | 10    | 2          |
| 11 | Science    | 9.4   | 2          |

SQLが存在します

数学のクラスで10年生を取得したすべての生徒を取得したいとします。

学生IDのみに関心がある場合は、次のようなクエリを実行できます。

SELECT
    student_grade.student_id
FROM
    student_grade
WHERE
    student_grade.grade = 10 AND
    student_grade.class_name = 'Math'
ORDER BY
    student_grade.student_id

ただし、アプリケーションはstudent識別子だけでなく、のフルネームを表示することに関心があるため、studentテーブルからの情報も必要です。

student数学で10グレードのレコードをフィルタリングするために、次のようにEXISTSSQL演算子を使用できます。

SELECT
    id, first_name, last_name
FROM
    student
WHERE EXISTS (
    SELECT 1
    FROM
        student_grade
    WHERE
        student_grade.student_id = student.id AND
        student_grade.grade = 10 AND
        student_grade.class_name = 'Math'
)
ORDER BY id

上記のクエリを実行すると、Alice行のみが選択されていることがわかります。

| id | first_name | last_name |
|----|------------|-----------|
| 1  | Alice      | Smith     |

外側のクエリstudentは、クライアントに返すことに関心のある行列を選択します。ただし、WHERE句は、関連する内部サブクエリでEXISTS演算子を使用しています。

EXISTS演算子は、サブクエリが少なくとも1つのレコードを返す場合はtrueを返し、行が選択されていない場合はfalseを返します。データベースエンジンは、サブクエリを完全に実行する必要はありません。単一のレコードが一致した場合、EXISTS演算子はtrueを返し、関連する他のクエリ行が選択されます。

student_gradeテーブルのstudent_id列が外側のstudentテーブルのid列と一致するため、内側のサブクエリは相関しています。


なんて素晴らしい答えでしょう。間違った例を使っていたので、コンセプトがわからなかったと思います。ないEXISTだけ相関サブクエリで動作しますか?のように、テーブルを1つだけ含むクエリで遊んでいましたSELECT id FROM student WHERE EXISTS (SELECT 1 FROM student WHERE student.id > 1)。私が書いたものは1つの単純なWHEREクエリで達成できることを知っていますが、私はそれを使用してEXISTSを理解していました。私はすべての行を取得しました。相関サブクエリを使用しなかったという事実が本当に原因ですか?ありがとう。
Bowen Liu

外部クエリのレコードをフィルタリングする必要があるため、相関サブクエリに対してのみ意味があります。あなたの場合、内部クエリはWHERE TRUEに置き換えることができます
Vlad Mihalcea

Vladに感謝します。私もそう思っていました。それは私がそれをいじっていたときに起こった奇妙な考えです。私は正直なところ、相関サブクエリの概念を知りませんでした。そして今、内側のクエリで外側のクエリの行を除外する方がはるかに理にかなっています。
Bowen Liu

0

EXISTSは、サブクエリが少なくとも1つの行を返すことを意味します。それだけです。その場合、外部テーブルのsupplier_idを内部テーブルのsupplier_idにチェックするため、相関サブクエリになります。このクエリは、事実上、次のように述べています。

すべてのサプライヤーを選択各サプライヤーIDについて、このサプライヤーの注文が存在するかどうかを確認します。サプライヤーが注文テーブルに存在しない場合は、結果からサプライヤーを削除します。注文テーブルに対応する行があるすべてのサプライヤーを返します。

この場合、INNERJOINを使用して同じことを行うことができます。

SELECT suppliers.* 
  FROM suppliers 
 INNER 
  JOIN orders 
    ON suppliers.supplier_id = orders.supplier_id;

ポニーのコメントは正しいです。その結合を使用してグループ化を行うか、必要なデータに応じて個別に選択する必要があります。


4
複数の子レコードが親に関連付けられている場合、内部結合はEXISTSとは異なる結果を生成します-それらは同一ではありません。
OMGポニー

私の混乱は、EXISTSを含むサブクエリがtrueまたはfalseを返すことを読んだことかもしれないと思います。しかし、それが返すのはこれだけではありませんよね?サブクエリは、すべての「注文テーブルに対応する行を持つサプライヤ」も返しますか?しかし、そうである場合、EXISTSステートメントはどのようにブール結果を返しますか?私が教科書で読んでいることはすべて、ブール値の結果しか返さないと言っているので、コードの結果を、返されると言われているものと一致させるのに苦労しています。
ダン

関数のようにEXISTSを読み取ります... EXISTS(resultset)。その場合、EXISTS関数は、結果セットに行がある場合はtrueを返し、空の場合はfalseを返します。基本的にはそれだけです。
David Fells

3
@ Dan、EXISTS()はすべてのソース行に対して個別に論理的に評価されることを考慮してください。これはクエリ全体の単一の値ではありません。
アルボ

-1

あなたが説明するのは、相関サブクエリを持ついわゆるクエリです。

(一般的に)代わりに結合を使用してクエリを作成することにより、回避する必要があります。

SELECT suppliers.* 
FROM suppliers 
JOIN orders USING supplier_id
GROUP BY suppliers.supplier_id

それ以外の場合、サブクエリは外部クエリの各行に対して実行されます。


2
これらの2つのソリューションは同等ではありません。orders結合条件に一致する行が複数ある場合、JOINはEXISTSサブクエリとは異なる結果を返します。
a_horse_with_no_name 2011年

1
代替ソリューションをありがとう。しかし、相関サブクエリと結合の間のオプションが与えられた場合、より効率的であるため、結合を使用する必要があることを提案しますか?
sunny_dev
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.