当子查询的结果集没有行时,A NOT EXISTS (subquery)
谓词将返回TRUE。当找到匹配的行时,它将返回FALSE。
从本质上讲,查询要求
对员工的每一行...从项目表检查每一行,看是否有在分配表中的一行行有匹配的EMPID Employee行上的empid和与Project表中的行匹配的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
)