我正在尝试学习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的行)。
在我看来,外部查询和子查询之间没有关系。
答案 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)
您可以使用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
答案 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。
假设我们在数据库中有以下两个表,它们构成一对多的表关系。
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 |
比方说,我们希望让所有获得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
因为否则,将对外部查询中的每一行执行子查询。