2013-02-01 48 views
4

我想知道这个查询是如何工作的:嵌套SQL查询,每个嵌套实际发生了什么?

​​

它应该返回谁被分配到每一个项目的所有员工的名字和它的工作不过我越来越困惑,如何/为什么它工作正常。

Schema是:

员工(EMPID INT,empName VARCHAR(100),作业VARCHAR(100),DEPTID INT,薪水INT);
已分配(empID INT,projID INT,角色VARCHAR(100));
项目(projID INT,标题VARCHAR(100),预算INT,基金INT);

我是SQL新手,所以详细/简单的解释将不胜感激。

回答

4

当我需要尝试了解发生了什么事情时,我寻找最内层的查询并向外工作。在你的情况,让我们开始:

SELECT empid, projid 
FROM Assigned 
WHERE empid = Employee.empid and projid = Project.projid 

这是匹配的分配表,其中EMPID和PROJID是前面表格(因此Employee.empid和Project.projid)中的所有记录。

假设项目表中有5个项目,Employee1分配给每个项目。这将返回5条记录。还假设Employee2被分配给这些项目中的一个,从而返回1条记录。

接下来看看:

SELECT projid FROM Project WHERE not exists (
     ... 
    ) 

现在,这(Employee1有5个项目,并和Employee2有1个项目)说,在以前的查询那些被发现的记录,然后从那里并没有项目表中的任何PROJID来自先前查询的任何匹配(不存在)。换句话说,Employee1将不返回此查询中的项目,但Employee2将返回4个项目。

最后,看

SELECT empname FROM Employee WHERE not exists (
     ... 
    ) 

正如第二查询,在前面的查询中发现的任何记录(没有记录到这些员工的所有项目,如Employee1和一些记录匹配,如果员工ISN分配给每个项目(如Employee2)),从Employee表中选择任何员工表中没有任何匹配的项目(再次,不存在)。换句话说,Employee1将返回,因为没有项目从前一个查询返回,并且Employee2不会返回,因为从上一个查询返回了一个或多个项目。

希望这会有所帮助。这里的一些其他信息存在:

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答案的问题 “是所有Y X TRUE?”

好运。

1

当子查询的结果集没有行时,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 
     )