SQL EXISTS语句如何工作?

时间:2011-05-01 06:31:41

标签: sql

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

  

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

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

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

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

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

7 个答案:

答案 0 :(得分:79)

这样想:

对于Suppliers中的“每一行”,检查Order表中是否存在满足条件Suppliers.supplier_id的行(这来自外部查询当前“行” )= Orders.supplier_id。当您找到第一个匹配的行时,请在此处停止 - WHERE EXISTS已满足。

外部查询和子查询之间的神奇链接在于Supplier_id从外部查询传递到每个评估的行的子查询。

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

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

答案 1 :(得分: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)

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

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

答案 2 :(得分:21)

您可以使用JOINEXISTSININTERSECT生成相同的结果:

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

答案 3 :(得分:6)

如果你的where子句看起来像这样:

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

您可以轻松理解为什么返回某些行而某些行不返回。

当where子句是这样的时候:

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

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

答案 4 :(得分:2)

这是一个很好的问题,所以我决定在自己的博客上写关于这个主题的a very detailed article

数据库表模型

假设我们在数据库中有以下两个表,它们构成一对多的表关系。

SQL EXISTS tables

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

student table包含以下两个记录:

| 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年级数学成绩的学生。

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

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表中的信息。

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

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运算符返回true;如果未选择任何行,则返回false。数据库引擎不必完全运行子查询。如果单个记录匹配,则EXISTS运算符返回true,并选择关联的其他查询行。

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

答案 5 :(得分:0)

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

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

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

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

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

答案 6 :(得分:0)

您所描述的是带有correlated subquery的所谓查询。

(一般情况下)您应该尝试通过使用连接编写查询来避免这种情况:

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

因为否则,将对外部查询中的每一行执行子查询。