2013-02-01 47 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 
     )