我想知道这个查询是如何工作的:
SELECT empname FROM Employee WHERE not exists (
SELECT projid FROM Project WHERE not exists (
SELECT empid, projid FROM Assigned WHERE empid = Employee.empid and projid = Project.projid
)
)
它应该返回分配给每个项目的所有员工的名字,但它确实有效,但是我对它如何/为什么正常工作感到困惑。
架构是:
员工(empID INT,empName VARCHAR(100),作业VARCHAR(100),deptID INT,工资INT);
已分配(empID INT,projID INT,角色VARCHAR(100));
项目(projID INT,标题VARCHAR(100),预算INT,资金INT);
我是SQL新手,所以详细/简单的解释将不胜感激。
答案 0 :(得分:4)
当我需要尝试了解发生了什么时,我会寻找最内在的查询并向外工作。在你的情况下,让我们从:
开始SELECT empid, projid
FROM Assigned
WHERE empid = Employee.empid and projid = Project.projid
这匹配了Assigned表中empid和projid在前面的表中的所有记录(因此是Employee.empid和Project.projid)。
假设Projects表中有5个项目,并且每个项目都分配了Employee1。这将返回5条记录。还假设Employee2被分配给其中一个项目,从而返回1个记录。
接下来看看:
SELECT projid FROM Project WHERE not exists (
...
)
现在,对于上一个查询中的那些找到的记录(Employee1有5个项目,Employee2有1个项目),请从Project表中选择任何与上一个查询没有任何匹配(不存在)的projid。换句话说,Employee1将不从此查询返回任何项目,但Employee2将返回4个项目。
最后,看看
SELECT empname FROM Employee WHERE not exists (
...
)
与第二个查询一样,对于在上一个查询中找到的任何记录(没有记录将这些员工与所有项目(如Employee1)匹配,如果员工没有分配给每个项目,如Employee2,则选择一些记录),选择Employee表中没有任何匹配的任何员工(同样,不存在)。换句话说,Employee1将返回,因为没有项目从上一个查询返回,并且Employee2不会返回,因为从上一个查询返回了一个或多个项目。
希望这会有所帮助。以下是有关EXISTS的其他信息:
http://dev.mysql.com/doc/refman/5.0/en/exists-and-not-exists-subqueries.html
从那篇文章中可以看出:
所有城市都有哪种商店?
SELECT DISTINCT store_type FROM stores s1 WHERE NOT EXISTS (
SELECT * FROM cities WHERE NOT EXISTS (
SELECT * FROM cities_stores
WHERE cities_stores.city = cities.city AND cities_stores.store_type = stores.store_type));
祝你好运。最后一个示例是双嵌套的NOT EXISTS查询。也就是说,它有 NOT EXISTS子句中的NOT EXISTS子句。在形式上,它回答 问题是“一个城市是否存在一个不在商店里的商店”? 但是更容易说嵌套的NOT EXISTS回答了这个问题 “对于所有人来说,这是真的吗?”
答案 1 :(得分:1)
当子查询的结果集没有行时,NOT EXISTS (subquery)
谓词将返回TRUE。当找到匹配的行时,它将返回FALSE。
基本上,查询是在询问
对于Employee中的每一行...检查Project表中的每一行,以查看Assigned表中是否有一行有一个empid与Employee行上的empid匹配的行和一个匹配的projid Project表中的一行。
只有找不到匹配的行时,才会返回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左侧的表1)中没有匹配项的任何行。对于这些行,如果存在匹配的行,则返回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
)