SQL EXISTS语句如何工作?

Dan*_*Dan 78 sql

我正在尝试学习SQL,并且很难理解EXISTS语句.我偶然发现了关于"存在"的引用并且不明白:

使用exists运算符,子查询可以返回零行,一行或多行,并且条件只检查子查询是否返回任何行.如果查看子查询的select子句,您将看到它包含一个文字(1); 由于包含查询中的条件只需要知道返回了多少行,因此子查询返回的实际数据无关紧要.

我不明白的是外部查询如何知道子查询检查哪一行?例如:

SELECT *
  FROM suppliers
 WHERE EXISTS (select *
                 from orders
                where suppliers.supplier_id = orders.supplier_id);
Run Code Online (Sandbox Code Playgroud)

我知道如果供应商和订单表中的id匹配,则子查询将返回true,并且将输出供应商表中匹配行的所有列.我不知道的是,如果只返回true或false,子查询如何传达应该打印哪个特定行(假设具有供应商ID 25的行).

在我看来,外部查询和子查询之间没有关系.

soj*_*jin 87

想一想:

对于'each'行Suppliers,检查表中是否存在Order满足条件的行Suppliers.supplier_id(这来自外查询当前'row')= Orders.supplier_id.当你找到第一个匹配的行时,停在那里 - WHERE EXISTS已经满足了.

外部查询和子查询之间的神奇链接在于,Supplier_id从外部查询传递到每个已计算的行的子查询.

或者,换句话说,子查询是针对外部查询的每个表行执行的.

它不像子查询在整体上执行并获得'true/false',然后尝试将此'true/false'条件与外部查询匹配.

  • 谢谢!"它不像在整体上执行子查询并获得'true/false',然后尝试将此'true/false'条件与outerquery匹配." 是什么真正为我清理了,我一直在想这是子查询的工作原理(很多时候他们都这样做),但你所说的是有道理的,因为子查询依赖于外部查询,因此必须每行执行一次 (5认同)

OMG*_*ies 31

在我看来,外部查询和子查询之间没有关系.

您认为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)
Run Code Online (Sandbox Code Playgroud)

......应该通过零误差击中除法,但不会.WHERE子句是EXISTS子句中最重要的部分.

另请注意,JOIN不是EXISTS的直接替代,因为如果有多个子记录与父级关联,则会有重复的父记录.

  • @Dan:退出EXISTS,在第一个匹配项上返回TRUE-因为供应商在ORDERS表中至少存在一次。如果由于在ORDERS中有多个子关​​系而希望查看SUPPLIER数据的重复,则必须使用JOIN。但是大多数人不希望重复,运行GROUP BY / DISTINCT可能会增加查询的开销。在SQL Server上,`EXISTS`比`SELECT DISTINCT ... FROM SUPPLIERS JOIN ORDERS ...`更有效,最近尚未在Oracle或MySQL上进行测试。 (2认同)

Ant*_*ull 22

您可以使用产生相同的结果JOIN,EXISTS,IN,或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
Run Code Online (Sandbox Code Playgroud)

  • 如果供应商有 1000 万行而订单有 1 亿行,您认为哪个查询会运行得更快,为什么? (2认同)

Vla*_*cea 8

数据库表模型

\n

让\xe2\x80\x99s 假设我们的数据库中有以下两个表,它们形成一对多表关系。

\n

SQL 存在表

\n

student表是父表,是student_grade子表,因为它有一个 Student_id 外键列引用学生表中的 id 主键列。

\n

其中student table包含以下两条记录:

\n
| id | first_name | last_name | admission_score |\n|----|------------|-----------|-----------------|\n| 1  | Alice      | Smith     | 8.95            |\n| 2  | Bob        | Johnson   | 8.75            |\n
Run Code Online (Sandbox Code Playgroud)\n

并且,该student_grade表存储学生获得的成绩:

\n
| id | class_name | grade | student_id |\n|----|------------|-------|------------|\n| 1  | Math       | 10    | 1          |\n| 2  | Math       | 9.5   | 1          |\n| 3  | Math       | 9.75  | 1          |\n| 4  | Science    | 9.5   | 1          |\n| 5  | Science    | 9     | 1          |\n| 6  | Science    | 9.25  | 1          |\n| 7  | Math       | 8.5   | 2          |\n| 8  | Math       | 9.5   | 2          |\n| 9  | Math       | 9     | 2          |\n| 10 | Science    | 10    | 2          |\n| 11 | Science    | 9.4   | 2          |\n
Run Code Online (Sandbox Code Playgroud)\n

SQL 存在

\n

假设\xe2\x80\x99s 表示我们想要获取数学课上获得 10 分的所有学生。

\n

如果我们只对学生标识符感兴趣,那么我们可以运行如下查询:

\n
SELECT\n    student_grade.student_id\nFROM\n    student_grade\nWHERE\n    student_grade.grade = 10 AND\n    student_grade.class_name = \'Math\'\nORDER BY\n    student_grade.student_id\n
Run Code Online (Sandbox Code Playgroud)\n

但是,应用程序有兴趣显示 a 的全名student,而不仅仅是标识符,因此我们student还需要表中的信息。

\n

为了过滤student数学成绩为 10 的记录,我们可以使用 EXISTS SQL 运算符,如下所示:

\n
SELECT\n    id, first_name, last_name\nFROM\n    student\nWHERE EXISTS (\n    SELECT 1\n    FROM\n        student_grade\n    WHERE\n        student_grade.student_id = student.id AND\n        student_grade.grade = 10 AND\n        student_grade.class_name = \'Math\'\n)\nORDER BY id\n
Run Code Online (Sandbox Code Playgroud)\n

运行上面的查询时,我们可以看到仅选择了 Alice 行:

\n
| id | first_name | last_name |\n|----|------------|-----------|\n| 1  | Alice      | Smith     |\n
Run Code Online (Sandbox Code Playgroud)\n

外部查询选择student我们感兴趣的行列返回给客户端。但是,WHERE 子句将 EXISTS 运算符与关联的内部子查询一起使用。

\n

如果子查询至少返回一条记录,则 EXISTS 运算符返回 true;如果没有选择任何行,则 EXISTS 运算符返回 false。数据库引擎不必完全运行子查询。如果匹配单个记录,则 EXISTS 运算符返回 true,并选择关联的其他查询行。

\n

内部子查询是相关的,因为表的 Student_id 列student_grade与外部 Student 表的 id 列匹配。

\n


Men*_*hem 7

如果你有一个where子句看起来像这样:

WHERE id in (25,26,27) -- and so on
Run Code Online (Sandbox Code Playgroud)

您可以很容易地理解为什么返回某些行而某些行不返回.

当where子句是这样的:

WHERE EXISTS (select * from orders where suppliers.supplier_id = orders.supplier_id);
Run Code Online (Sandbox Code Playgroud)

它只是意味着:返回订单表中具有相同ID的现有记录的行.


Dav*_*lls 0

EXISTS 意味着子查询至少返回一行,确实如此。在这种情况下,它是一个相关子查询,因为它检查外表的supplier_id 到内表的supplier_id。该查询实际上表示:

SELECT 所有供应商 对于每个供应商 ID,查看该供应商是否存在订单 如果供应商不在订单表中,则从结果中删除该供应商 返回在订单表中具有相应行的所有供应商

在这种情况下,您可以使用 INNER JOIN 执行相同的操作。

SELECT suppliers.* 
  FROM suppliers 
 INNER 
  JOIN orders 
    ON suppliers.supplier_id = orders.supplier_id;
Run Code Online (Sandbox Code Playgroud)

小马的评论是正确的。您需要使用该连接进行分组,或者根据您需要的数据选择不同的。

  • 如果多个子记录与父记录关联,则内部联接将产生与 EXISTS 不同的结果——它们不相同。 (4认同)
  • @Dan,请考虑 EXISTS() 在逻辑上独立地对*每个*源行进行评估 - 它不是整个查询的单个值。 (3认同)