这很简单,当你掌握了它的窍门时:
SELECT s.S_Fname, s.S_Lname
FROM STUDENT s
WHERE s.S_Sex = 'F'
AND S.S_Id NOT IN(SELECT e.S_Id -- take this line
FROM ENROLLMENT e
WHERE e.Mark < 70);
该行基本上与来自子查询S.S_Id
的所有值进行比较。e.S_Id
现在将其更改为NOT EXISTS
并在子查询中放置一个相等检查S.S_Id = e.S_Id
:
SELECT s.S_Fname, s.S_Lname
FROM STUDENT s
WHERE s.S_Sex = 'F'
AND NOT EXISTS (SELECT e.S_Id
FROM ENROLLMENT e
WHERE (e.Mark < 70) -- if this is complex, you'll need parentheses
AND S.S_Id = e.S_Id);
微小的可能变化是意识到(SELECT e.S_Id ...
并不真正需要e.S_Id
. 子查询EXISTS
只NOT EXISTS
检查是否有行返回,列值无关紧要。您可以在SELECT *
那里放置一个常量(SELECT 1
很常见)SELECT NULL
甚至SELECT 1/0
(是的,这会起作用!):
SELECT s.S_Fname, s.S_Lname
FROM STUDENT s
WHERE s.S_Sex = 'F'
AND NOT EXISTS (SELECT 1
FROM ENROLLMENT e
WHERE e.Mark < 70
AND S.S_Id = e.S_Id);
另一个主要考虑因素是,当您以这种方式进行转换时,只有当两列都不可为空时,查询的(看似等效的)NOT EXISTS
和NOT IN
写入才真正等效。S_Id
如果该e.S_Id
列可以为空,则NOT IN
可能导致整个查询根本不返回任何行(因为当其中一个x NOT IN (a, b, c, ...)
为x<>a AND x<>b AND ...
时,该条件不能为真a,b,c...
。NULL
)
出于类似的原因,如果 可以为空,您将得到不同的结果s.S_Id
(在这种情况下不太可能,因为它可能是主键,但在其他情况下很重要。)
因此,使用 几乎总是更好NOT EXISTS
,因为即使任一列可以为空(S.S_Id = e.S_Id
检查将丢弃之前为 null 的行),它的行为也会有所不同,并且通常这种行为是想要的行为。问题中有很多细节:NOT IN vs NOT EXISTS,@Martin Smith 的回答。您还将找到将其转换NOT IN
为NOT EXISTS
并保持与 null 相关(不愉快)行为的方法。