當子查詢的結果集沒有行時,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
)