带有连接条件的 OUTER JOIN 导致意外结果

blo*_*les 2 join sql-server

SQL Server 2008 R2,但在所有其他版本的 SQL Server 上也发现了可能的行为。

这似乎很明显,但对我来说,这似乎是一个错误。

以下查询给出了意想不到的结果,这是设置:

CREATE TABLE #Base (
Key1 int,
RefDate date
)


CREATE TABLE #JoinTable (
    Key1 int,
    RefDate date
    )

INSERT INTO #Base
SELECT 1, '2012-05-05'
UNION
SELECT 2, '2013-06-06'
UNION
SELECT 3, '2014-07-07'
UNION
SELECT 4, '2015-08-08'
UNION
SELECT 5, '2016-09-09'


INSERT INTO #JoinTable
SELECT 4, '2012-05-05'
UNION
SELECT 5, '2013-06-06'
UNION
SELECT 6, '2014-07-07'
UNION
SELECT 7, '2015-08-08'
UNION
SELECT 8, '2016-09-09'
Run Code Online (Sandbox Code Playgroud)

以下查询按我的预期执行,返回仅出现在基表中的 3 行:

SELECT * 
FROM #Base b
LEFT OUTER JOIN #JoinTable j ON b.Key1 = j.Key1
WHERE j.Key1 IS NULL
Run Code Online (Sandbox Code Playgroud)

现在我想将行限制为 2014 年 1 月 1 日之前的行。据我所知,我可以采用以下两种方法,两者都应该是完全可行的:

SELECT b.*
FROM #Base b
LEFT OUTER JOIN #JoinTable j ON b.Key1 = j.Key1 AND b.RefDate < '2014-01-01'
WHERE j.Key1 IS NULL


SELECT b.* 
FROM #Base b
LEFT OUTER JOIN #JoinTable j ON b.Key1 = j.Key1 
WHERE j.Key1 IS NULL AND b.RefDate < '2014-01-01'
Run Code Online (Sandbox Code Playgroud)

但是,只有第二个查询返回我想要的数据。第一个似乎忽略了连接行中 AND 之后添加的连接条件。

为什么?

(sql小提琴链接在这里

Vla*_*nov 6

这不是一个错误,它是如何LEFT JOIN工作的。

b LEFT JOIN j ON <SomeCondition>
Run Code Online (Sandbox Code Playgroud)

b无论连接条件如何,都将返回所有行。连接条件仅定义/限制将从中返回的行j


SELECT b.*
FROM #Base b
LEFT OUTER JOIN #JoinTable j ON b.Key1 = j.Key1 AND b.RefDate < '2014-01-01'
WHERE j.Key1 IS NULL
Run Code Online (Sandbox Code Playgroud)

你可以这样想:

  1. 遍历#Base.
  2. 对于中的每一行,#Base查找#JoinTable满足连接条件 ( b.Key1 = j.Key1 AND b.RefDate < '2014-01-01') 的所有行。中可能没有这样的行#JoinTable,在这种情况下返回NULLs。请注意,连接条件不过滤#Base,它只过滤#JoinTable

当你把b.RefDate < '2014-01-01'进入WHERE,那么你过滤#Base