如何更快地执行SQ​​L“NOT IN”查询?

时间:2012-02-10 16:11:09

标签: sql sql-server database

我有一个电子邮件地址表(EMAIL):

EmailAddress
------------
jack@aol.com
jill@aol.com
tom@aol.com
bill@aol.lcom

以及黑名单电子邮件地址的表格(黑名单):

EmailAddress
------------
jack@aol.com
jill@aol.com

我希望选择EMAIL表中但不在BLACKLIST表中的那些电子邮件地址。我在做:

SELECT EmailAddress
FROM EMAIL
WHERE EmailAddress NOT IN
   (
      SELECT EmailAddress
      FROM BLACKLIST
   )

但是当行数非常高时,性能非常糟糕。

我怎样才能做得更好? (如果可能,假设使用通用SQL。如果没有,则假定使用T-SQL。)

4 个答案:

答案 0 :(得分:27)

您可以使用左外连接或not exists子句。

左外连接:

select E.EmailAddress
  from EMAIL E left outer join BLACKLIST B on (E.EmailAddress = B.EmailAddress)
 where B.EmailAddress is null;

不存在:

select E.EmailAddress
  from EMAIL E where not exists
         (select EmailAddress from BLACKLIST B where B.EmailAddress = E.EmailAddress)

两者都是非常通用的SQL解决方案(不依赖于特定的数据库引擎)。我会说后者的性能更高一些(尽管不是很多)。但绝对比not in更高效。

正如评论者所述,您还可以尝试在BLACKLIST(EmailAddress)上创建索引,这有助于加快查询的执行速度。

答案 1 :(得分:4)

如果黑名单允许空值为EmailAddress,则NOT IN与NOT EXISTS不同。如果存在单个空值,则查询结果将始终返回零行,因为每个值的NOT IN(null)都是unknown / false。因此,查询计划略有不同,但我认为不会对性能造成严重影响。

建议创建一个名为VALIDEMAIL的新表,向BLACKLIST添加一个触发器,在插入行时从VALIDEMAIL中删除地址,并在从BLACKLIST中删除时添加到VALIDEMAIL。然后用一个VALIDEMAIL和BLACKLIST联合的视图替换EMAIL。

答案 2 :(得分:1)

select E.EmailAddress
  from EMAIL E where not exists
         (select EmailAddress from BLACKLIST B where B.EmailAddress = E.EmailAddress)

等于 (顺便说一句,可能是所有者)

select EmailAddress from mail.EMAIL 
EXCEPT
select EmailAddress from mail.BLACKLIST 

即使在EmailAddress中为NULL,也会为您提供不同的行

答案 3 :(得分:1)

enter image description here

这只是告诉您的一种非常冗长的方式,对于在表 B 中不存在某些条件的情况下查找表 A 中的所有行的模式,NOT EXISTS 通常将是您的最佳选择。但是,一如既往,您需要在自己的环境中测试这些模式,使用您的架构、数据和硬件,并与您自己的工作负载混合。

更多详情 https://sqlperformance.com/2012/12/t-sql-queries/left-anti-semi-join