使用 LEFT JOIN 或 NOT EXISTS 之间的最佳实践

Mic*_*son 88 join sql-server exists

使用 LEFT JOIN 或 NOT EXISTS 格式之间是否有最佳实践?

使用一个比另一个有什么好处?

如果没有,应该首选哪个?

SELECT *
FROM tableA A
LEFT JOIN tableB B
     ON A.idx = B.idx
WHERE B.idx IS NULL
Run Code Online (Sandbox Code Playgroud)
SELECT *
FROM tableA A
WHERE NOT EXISTS
(SELECT idx FROM tableB B WHERE B.idx = A.idx)
Run Code Online (Sandbox Code Playgroud)

我在 Access 中对 SQL Server 数据库使用查询。

Han*_*non 68

最大的区别是不是在加入VS不存在,它是(书面),该SELECT *

在第一个例子中,你得到的所有列 AB,而在第二个例子,你从只列A

在 SQL Server 中,在一个非常简单的人为示例中,第二个变体稍微快一点:

创建两个示例表:

CREATE TABLE dbo.A
(
    A_ID INT NOT NULL
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
);

CREATE TABLE dbo.B
(
    B_ID INT NOT NULL
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
);
GO
Run Code Online (Sandbox Code Playgroud)

在每个表中插入 10,000 行:

INSERT INTO dbo.A DEFAULT VALUES;
GO 10000

INSERT INTO dbo.B DEFAULT VALUES;
GO 10000
Run Code Online (Sandbox Code Playgroud)

从第二个表中每 5 行删除一次:

DELETE 
FROM dbo.B 
WHERE B_ID % 5 = 1;

SELECT COUNT(*) -- shows 10,000
FROM dbo.A;

SELECT COUNT(*) -- shows  8,000
FROM dbo.B;
Run Code Online (Sandbox Code Playgroud)

执行两个测试SELECT语句变体:

SELECT *
FROM dbo.A
    LEFT JOIN dbo.B ON A.A_ID = B.B_ID
WHERE B.B_ID IS NULL;

SELECT *
FROM dbo.A
WHERE NOT EXISTS (SELECT 1
    FROM dbo.B
    WHERE b.B_ID = a.A_ID);
Run Code Online (Sandbox Code Playgroud)

执行计划:

在此处输入图片说明

第二个变体不需要执行过滤操作,因为它可以使用左反半连接运算符。


Rob*_*ley 31

从逻辑上讲,它们是相同的,但NOT EXISTS更接近您要求的 AntiSemiJoin,并且通常是首选。它还更好地强调了您无法访问 B 中的列,因为它仅用作过滤器(而不是让它们具有 NULL 值)。

许多年前(SQL Server 6.0 ish)LEFT JOIN速度更快,但很长一段时间以来情况并非如此。这些天,NOT EXISTS速度略快。


Access 中最大的影响是该JOIN方法必须在过滤之前完成联接,在内存中构造联接集。使用NOT EXISTS它检查行但不为列分配空间。另外,一旦找到一行,它就会停止查找。Access 中的性能变化更大一些,但一般的经验法则是,它NOT EXISTS往往会更快一些。我不太愿意说这是“最佳实践”,因为涉及的因素更多。

  • “Access 中最大的影响是 `JOIN` 方法必须在过滤之前完成连接” 这也适用于 SQL Server (2012),当表 B 有很多行时,`LEFT JOIN 的乘法性质` 毫不夸张地说,可以让事情变得不成比例:在一个测试用例中,我发现我的表 A 大约有 3000 行,表 B 大约有 250K 行,生成了超过 7.5 亿(!)行的组合集。使用“NOT EXISTS”,可以预先应用过滤,并且总结果集永远不会超过任一表中的行数。 (3认同)

pim*_*pim 7

我注意到的一个例外是使用Linked Servers时的NOT EXISTS优越性(但略有不同)。LEFT JOIN ... WHERE IS NULL

从检查执行计划来看,NOT EXISTS运算符似乎是以嵌套循环方式执行的。因此它是按行执行的(我认为这是有道理的)。

演示此行为的示例执行计划: 在此处输入图片说明

  • 链接服务器对于这种事情来说是残酷的。解决该问题的一种可能方法是使用简单的“INSERT INTO #t (a,b,c) SELECT a,b,c FROM LinkedServer.database.dbo.table WHERE x=”通过链接服务器链接复制远程数据y`,然后针对数据库的临时副本运行“NOT EXISTS (...)”子句。 (2认同)

cro*_*sek 5

通常,引擎将创建一个基本上基于以下内容的执行计划:

  1. A 和 B 中的行数
  2. A 和/或 B 上是否有索引。
  3. 结果行(和中间行)的预期数量
  4. 输入查询的形式(即你的问题)

对于(4):

“不存在”计划鼓励在表 B 上进行基于查找的计划。当表 A 小而表 B 大(并且索引存在于 B 上)时,这是一个不错的选择。

当表 A 非常大或表 B 非常小或 B 上没有索引并返回大结果集时,“反联接”计划是一个不错的选择。

然而,它只是一种“鼓励”,就像加权输入一样。强 (1)、(2)、(3) 常常使选择 (4) 没有实际意义。

(忽略您的示例由于 * 返回不同列的影响,由@MaxVernon 回答解决。)。