NOT IN 与连接列

NOT IN vs concatenate columns

SQL下面的两个不一样吗?我的意思是功能明智应该做同样的事情?

我期待这第一个 SQL 应该也有结果。

SELECT * 
FROM #TEST 
WHERE COL1 NOT IN (SELECT COL1 FROM #TEST_1) 
  AND COL2 NOT IN (SELECT COL2 FROM #TEST_1) 

--1 record

SELECT * 
FROM #TEST 
WHERE COL1 + COL2 NOT IN (SELECT COL1  +COL2 FROM #TEST_1)



CREATE TABLE #TEST 
(
    COL1 VARCHAR(10), 
    COL2 VARCHAR(10), 
    COL3 VARCHAR(10)
) 

INSERT INTO #TEST VALUES ('123', '321', 'ABC')
INSERT INTO #TEST VALUES ('123', '436', 'ABC')

CREATE TABLE #TEST_1
(
    COL1 VARCHAR(10), 
    COL2 VARCHAR(10), 
    COL3 VARCHAR(10)
) 

INSERT INTO #TEST_1 VALUES ( '123','532','ABC')
INSERT INTO #TEST_1 VALUES ( '123','436','ABC')

--No result
SELECT * 
FROM #TEST 
WHERE COL1 NOT IN (SELECT COL1 FROM #TEST_1) 
  AND COL2 NOT IN (SELECT COL2 FROM #TEST_1) 

--1 record
SELECT * 
FROM #TEST 
WHERE COL1 + COL2 NOT IN (SELECT COL1 + COL2 FROM #TEST_1)

让我们把它放到更多的上下文中,看看你的 2 个 WHERE 子句,我将分别称之为“WHERE 1”和“WHERE 2”:

--WHERE 1
WHERE COL1 NOT IN (SELECT COL1 FROM #TEST_1) 
  AND COL2 NOT IN (SELECT COL2 FROM #TEST_1) 

--WHERE 2
WHERE COL1 + COL2 NOT IN (SELECT COL1 + COL2 FROM #TEST_1)

您可能已经注意到,此 do 的行为不同。事实上,从逻辑角度和数据库引擎处理它们的方式来看,它们完全不同。

WHERE 2,首先是 not SARGable。这意味着您的 table 上的任何索引都将无法使用,数据引擎将不得不扫描 整个 table。但是,对于 WHERE 1,它 SARGable,如果您有任何索引,它们可用于执行查找,可能有助于提高性能。

从逻辑的角度来看,我们先看WHERE 2。这要求 COL1COL2 的连接值不匹配 COL1COL2 的另一个连接值;这意味着这些值必须在同一行。因此 '123456' 仅当 Col1 具有值 '123'Col2 具有值 '456'.

时才会匹配

然而对于WHERE 1,这里Col1的值需要在另一个table中找不到,Col2也需要找不到,但是他们可以在 不同的 行。这就是不同之处。由于 Col1 中的 '123' 出现在两个 table 中(并且是唯一的值),因此 NOT IN 未满足并且不返回任何行。

如果您想要 WHERE 2 的 SARGable 版本,我建议使用 EXISTS:

--1 row
SELECT T.COL1, --Don't use *, specify your columns
       T.COL2, --Qualifying your columns is important!
       T.COL3
FROM #TEST T --Aliasing is important!
WHERE NOT EXISTS (SELECT 1
                  FROM #TEST_1 T1
                  WHERE T1.COL1 = T.COL1
                    AND T1.COL2 = T.COL2);

db<>fiddle

当您以这种方式添加字符串时(使用 + 而不是 concatenation),它会添加两个字符串并为您提供数值。 在第一个查询中,您没有添加字符串,所以您所做的是:

Select all rows from #Test that values of Col1 and Col2 are not in Test1 实际上,只有第一个参数会删除所有内容,因为您在 col1.

的两个表中都有 123 个值

第二个查询对该字符串求和,但不是通过串联求和。 它实际上在幕后将 varchars 转换为数字。 所以第二个查询是:

Select all rows from #test where COL1+COL2 (its 444 at first row, and 559 in second row) are not in #Test 1

如果您在#Test1 添加行,则值为:

For the first row COL1+COL2= 655

For the second row COL1+COL2= 559

所以只有总和为 444 的行不在 #Test1,这就是为什么你得到 1 行作为结果。

总结一下:

这就是为什么您在第二次查询中只看到 1 行,而在第一次查询中看不到任何记录的原因。在第一个查询中,只有第一个条件真正有效并削减了一切。在第二个查询中 SQL 引擎正在将 varchars 转换为数字。

所以'123'+'321'不是'123321'而是'444'。