NOT EXISTS与NOT IN和LEFT JOIN WHERE为NULL之间有什么区别?


151

在我看来,您可以使用NOT EXISTS,NOT IN或LEFT JOIN WHERE IS NULL在SQL查询中执行相同的操作。例如:

SELECT a FROM table1 WHERE a NOT IN (SELECT a FROM table2)

SELECT a FROM table1 WHERE NOT EXISTS (SELECT * FROM table2 WHERE table1.a = table2.a)

SELECT a FROM table1 LEFT JOIN table2 ON table1.a = table2.a WHERE table1.a IS NULL

我不确定所有语法是否正确,但这是我所见过的常规技术。我为什么选择一个使用另一个?性能会有所不同吗?哪一个是最快/最有效的?(如果取决于实现,我什么时候会使用每个?)


6
许多常见的SQL引擎使您能够查看执行计划。通过这种方式,您通常可以发现逻辑上等效的查询在效率上的显着差异。任何方法的成功都取决于诸如表大小,存在哪些索引等因素。
克里斯·法默

2
@wich:没有数据库关心您在EXISTS子句中返回的确切内容。您可以返回*NULL或任何其他方式:所有这些都将被优化。
Quassnoi

2
@wich-为什么?两者都在这里:techonthenet.com/sql/exists.php和这里:msdn.microsoft.com/en-us/library/ms188336.aspx似乎使用* ...
froadie 2010年

8
@wich:这与“表达兴趣”无关。这是关于查询解析器要求您在SELECT和之间放置一些内容的FROM。而且*更容易输入。是的,SQL确实与自然语言有些相似,但是它是由一台机器(一台编程的机器)解析并执行的。这并不是说它会突然闯入您的隔间并大喊“停止EXISTS查询中多余的字段,因为我讨厌解析它们然后扔掉它们!”。真的,用电脑还可以。
Quassnoi

1
@Quassnoi,如果您仅出于解释机器的目的而编写代码,则该代码看起来会很恐怖,不幸的是,很多人都这样工作。但是,如果您用另一种语言编写代码,编写代码以表达您希望机器作为对等体的公报,那么您将编写更好,更可维护的代码。要聪明,为人而不是为计算机编写代码。
至极

Answers:


139

简而言之:

NOT IN有点不同:如果NULL列表中只有一个,它将永远不会匹配。

  • 在中MySQLNOT EXISTS效率较低

  • 在中SQL ServerLEFT JOIN / IS NULL效率较低

  • 在中PostgreSQLNOT IN效率较低

  • 在中Oracle,所有三种方法都相同。


1
感谢您的链接!并感谢您的快速概述...我的办公室由于某些原因而阻止了该链接:P,但是一旦我连接到常规计算机,便会立即检查出来。
froadie 2010年

2
另一点是,如果table1 .a包含NULLEXISTS查询将不返回该行,但是NOT IN查询将为table2空。NOT IN与NOT EXISTS可空列:SQL Server
Martin Smith

@MartinSmith:NULL NOT IN ()计算结果为true(不是NULL),就像NOT EXISTS (NULL = column)
Quassnoi

2
@Quassnoi-嗯,好点了,弄错了方向。在NOT EXISTS将总是返回行,但NOT IN只能这样做,如果子查询没有返回行。
马丁·史密斯

5

如果数据库擅长优化查询,则将前两个转换成接近第三个。

对于您所质疑的简单情况,应该几乎没有差异,因为它们将全部作为联接执行。在更复杂的查询中,数据库可能无法通过not innot exists查询进行联接。在这种情况下,查询速度会慢很多。另一方面,如果没有可以使用的索引,则联接也可能会执行不佳,因此,仅仅因为您使用联接并不意味着您是安全的。您将必须检查查询的执行计划,以判断是否存在任何性能问题。


2

假设您避免使用空值,那么它们都是使用标准SQL 编写联接的所有方法。

一个明显的遗漏是等效于使用EXCEPT

SELECT a FROM table1
EXCEPT
SELECT a FROM table2

注意,在Oracle中,您需要使用MINUS运算符(可以说是更好的名称):

SELECT a FROM table1
MINUS
SELECT a FROM table2

说到专有语法,根据您使用的产品OUTER APPLY(例如在SQL Server中),可能还会有一些非标准的等效项目值得研究:

SELECT t1.a
  FROM table1 t1
       OUTER APPLY 
       (
        SELECT t2.a
          FROM table2 t2
         WHERE t2.a = t1.a
       ) AS dt1
 WHERE dt1.a IS NULL;

0

当需要在具有多字段主键的表中插入数据时,考虑到不检查“表中不存在具有'this'值的记录”会更快(我在Access中尝试过,但我认为在任何数据库中), -只需插入表中,多余的记录(通过键)将不会被插入两次。


0

从性能角度来看,始终避免使用诸如NOT IN,NOT EXISTS等反向关键字,因为DBMS需要检查所有可用的反向项目,并删除反向选择。


1
实际需要时,您提出什么解决方法NOT
dnoeth '16

好吧,当没有原因的选择时,我们需要使用NOT运算,这就是它们存在的原因。最佳做法是在我们有其他替代解决方案时避免使用它们。
Lahiru Cooray

@onedaywhen,如果优化器转换查询并返回错误的结果,则它是一个错误
DavidדודוMarkovitz 16-10-9

@DuduMarkovitz:是的,如果您与SQL Server团队联系,并且他们承认该错误但拒绝修复,因为他们说这样做可能会使查询运行速度变慢,那么这是您需要处理的错误。
2016年

@onedaywhen-我认为这不是一种假设的情况:-)您是否还记得错误的详细信息?
DavidדודוMarkovitz 2016年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.