当NOT EXISTS (subquery)
子查询的结果集没有行时,谓词将返回 TRUE。当找到匹配的行时,它将返回 FALSE。
本质上,查询是在询问
对于 Employee 中的每一行...检查 Project 表中的每一行,以查看 Assigned 表中是否有一行的 empid 与 Employee 行上的 empid 和 projid 匹配项目表。
仅当未找到匹配行时,才会返回来自 Employee 的行。
注意,子查询的 SELECT 列表中的表达式并不重要;正在检查的只是该子查询是否返回一(或多)行。通常,我们在 SELECT 列表中使用文字 1;这提醒我们,我们正在检查的是是否找到了一行。)
我通常会以如下所示的样式编写该查询:
SELECT e.empname
FROM Employee e
WHERE NOT EXISTS
( SELECT 1
FROM Project p
WHERE NOT EXISTS
( SELECT 1
FROM Assigned a
WHERE a.empid = e.empid
AND a.projid = p.projid
)
)
我将“ SELECT 1
”读为“选择一行”)
该查询的结果集本质上等同于该查询的结果集(通常效率低得多):
SELECT e.empname
FROM Employee e
WHERE e.empid NOT IN
( SELECT a.empid
FROM Assigned a
JOIN Project p
ON a.projid = p.projid
WHERE a.empid IS NOT NULL
GROUP
BY a.empid
)
该NOT IN
查询可能更容易理解,因为您可以运行该子查询并查看它返回的内容。(NOT EXISTS 子查询可能有点令人困惑的是,在 SELECT 列表中返回什么表达式并不重要;重要的是是否返回一行。)NOT IN 有一些“陷阱”子查询除了性能很差;您需要小心确保子查询不返回 NULL 值,因为这样 NOT IN (NULL,...) 将永远不会返回 true。
也可以使用反连接模式返回等效的结果集:
SELECT e.empname
FROM Employee e
LEFT
JOIN ( SELECT a.empid
FROM Assigned a
JOIN Project p
ON a.projid = p.projid
WHERE a.empid IS NOT NULL
GROUP
BY a.empid
) o
ON o.empid = e.empid
WHERE o.empid IS NULL
在该查询中,我们正在 empid 上查找“匹配项”。LEFT 关键字告诉 MySQL 从 Employee(JOIN 左侧的表)返回任何不匹配的行。对于这些行,将返回 NULL 值来代替如果存在匹配行时将返回的列的值。然后,“技巧”是丢弃所有匹配的行。我们通过检查如果存在匹配则不会为 NULL 的列中的 NULL 来做到这一点。
如果我要使用NOT EXISTS
谓词编写这个查询,我可能实际上更喜欢这样写:
SELECT e.empname
FROM Employee e
WHERE NOT EXISTS
( SELECT 1
FROM Assigned a
JOIN Project p
ON a.projid = p.projid
WHERE a.empid = e.empid
)