为什么 SELECT Distinct 对于 SQL 列对于整数值 0 的行为异常?

Why does SELECT Distinct for a SQL column behave unexpectedly for the integer value of 0?

我 运行 这个在 SQL Server Express 上:

DECLARE @Logs table(Id INT, Num INT)
-- For VALUES (1,1),(2,x), (3,x),(4,x), version 1 and version 2 return the same value. EXCEPT IF x=0...Why????
INSERT INTO @Logs (Id, Num) VALUES (1,1),(2,0), (3,0),(4,0) --LINE 2

---------Version 1-------------
;WITH table2(Num,rn) as (
    SELECT Num, ROW_NUMBER() OVER(ORDER BY (SELECT 1)) as rn from @Logs
)
SELECT DISTINCT(t3.Num) from table2 as t3 JOIN ( --DISTINCT on this line is only difference
    SELECT t1.Num, t2.rn as last_rn, t1.rn as second_rn from
    table2 as t1 JOIN table2 as t2 ON t1.rn = t2.rn-1 AND t1.Num = t2.Num
) as t4 ON t4.second_rn - 1 = t3.rn AND t3.Num = t4.Num

-----------Version 2 ------------
;WITH table2(Num,rn) as (
    SELECT Num, ROW_NUMBER() OVER(ORDER BY (SELECT 1)) as rn from @Logs
)

SELECT t3.Num as ConsecutiveNums from table2 as t3 JOIN (
    SELECT t1.Num, t2.rn as last_rn, t1.rn as second_rn from
    table2 as t1 JOIN table2 as t2 ON t1.rn = t2.rn-1 AND t1.Num = t2.Num
) as t4 ON t4.second_rn - 1 = t3.rn AND t3.Num = t4.Num

版本 1 returns 1 个单行,值为 0。版本 2 returns 没有行。但是,两者之间唯一的区别是 SELECT 语句开头的关键字 DISTINCT

但是,当我将 INSERT INTO @Logs(第 2 行)中的 0 替换为 0 以外的任何数字时,版本 1 和版本 2 return 同一行。

为什么???我检查了一个简单的 SELECT DISTINCT 在数字零上没有同样的错误( SELECT DISTINCT(n) from (VALUES (1),(2),(0),(3),(2)) as t(n) 表现符合预期)

这里发生了几件事。

首先,SQL 表和结果集表示 无序 集。没有顺序,除非有明确的 ORDER BY.

因此,正如 Jeroen 在评论中指出的那样,CTE 具有不确定的排序。

其次,SQL服务器没有具体化子查询。它在每次被引用时运行子查询。现在,将这一点与第一点结合起来,您就会意识到 CTE 每次在查询中被引用时都会 return 不同的结果,这取决于优化器和底层执行引擎的变幻莫测。

所以,这就是正在发生的事情。 SQL 每次引用 CTE 时,服务器都会为其选择不同的执行计划,从而导致数据不兼容——以及不同的结果。我会注意到这在 SQL Server 和 SQL Server Express 中都是正确的(参见 here)。

将联接类型更改为 left join 无法解决问题 。它可能会生成预期的执行计划,但这会隐藏问题。

正确的更改是修复 order by,大概在这种情况下使用 order by id:

WITH table2(Num,rn) as (
      SELECT Num, ROW_NUMBER() OVER (ORDER BY (SELECT id)) as rn 
      FROM @Logs
     )

请注意,这些由 ORDER BY 中的“联系”(通常但不总是与 window 函数一起使用)引起的错误可能很难找到和调试。我会敦促您注意排序并始终思考“键是唯一的,我是否应该添加一个主键作为键的最后一个顺序”。