将NOT IN转换为NOT EXISTS

时间:2013-05-23 06:43:51

标签: sql oracle

有一个时间的噩梦了解NOT EXISTS的用法,主要是如何转换下面的NOT IN解决方案,这样我才能真正理解我是如何实现结果的。 有几篇关于askTom,oracle论坛和stackoverflow的文章,但找不到任何明显有助于理解这个问题的文章。如果我通过无聊的搜索错过了它,我很抱歉。

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
        FROM ENROLLMENT e
        WHERE e.Mark < 70);

对内容提供一些帮助,试图找到在他们注册的任何课程中从未获得过低于70分的女学生。

2 个答案:

答案 0 :(得分:13)

当你掌握它时,这很简单:

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。使用EXISTSNOT 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 INS_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相关(令人不快)行为的方法。

答案 1 :(得分:4)

不完全是你要求的,但是这里有一种方法可以在没有NOT EXISTS的情况下做到这一点 - 创建'70以下的成就者'作为派生表,LEFT OUTER JOIN并检查这样的空值。 ..

SELECT s.S_Fname, s.S_Lname 
FROM STUDENT s
LEFT JOIN
(
  SELECT S_Id 
  FROM ENROLLMENT
  WHERE Mark < 70
) e ON e.S_Id = s.S_Id
WHERE e.S_Id IS NULL 
AND s.S_Sex = 'F';

也可能有助于您对SQL的一般理解。有优点和缺点对两种方法都有所帮助。

Click here to go to SQL Fiddle