SQL Server - 按空值划分的 ROW_NUMBER 分区的行为

Sim*_*onN 4 sql-server null row-number

我发现这种行为非常奇怪且违反直觉。(即使对于 SQL)。

set ansi_nulls off
go
;with sampledata(Value, CanBeNull) as
(
  select 1, 1
  union 
  select 2, 2
  union 
  select 3, null
  union 
  select 4, null
  union 
  select 5, null
  union 
  select 6, null
)
select ROW_NUMBER() over(partition by CanBeNull order by      value) 'RowNumber',* from sampledata
Run Code Online (Sandbox Code Playgroud)

哪个返回

1   3   NULL
2   4   NULL
3   5   NULL
4   6   NULL
1   1   1
1   2   2
Run Code Online (Sandbox Code Playgroud)

这意味着为了计算行号,所有空值都被视为同一组的一部分。SET ANSI_NULLLS 是打开还是关闭都没有关系。但是,根据定义,null 是完全未知的,那么如何将 null 像这样组合在一起呢?据说为了将事物按等级顺序排列,苹果和橙子以及负 1 的平方根和量子黑洞或任何可以有意义地排序的东西。一些实验表明,第一列用于生成排名顺序为

  select 1, '1'
  union 
  select 2, '2'
  union 
  select 5, null
  union 
  select 6, null
  union
  select 3, null
  union 
  select 4, null
Run Code Online (Sandbox Code Playgroud)

生成相同的值。这具有重大影响,导致我正在处理的遗留代码出现问题。这是预期的行为吗,除了用唯一值替换 select 查询中的 null 之外,还有什么方法可以减轻它吗?

我所期望的结果是

1   3   NULL
1   4   NULL
1   5   NULL
1   6   NULL
1   1   1
1   2   2
Run Code Online (Sandbox Code Playgroud)

使用 Dense_Rank() 没有区别。

小智 6

哟。

所以问题是,当 T-SQL 处理谓词中的 NULL 时,它使用三元逻辑(TRUE、FALSE 或 UNKNOWN)并显示您声明的您期望从查询中得到的行为。但是,在对值进行分组时,T-SQL 将 NULL 视为一组。因此,您的查询会将 NULL 分组在一起并开始对该窗口中的行进行编号。

对于您说希望看到的结果,此查询应该有效...

WITH sampledata (Value, CanBeNull)
AS
(
    SELECT 1, 1
    UNION
    SELECT 2, 2
    UNION
    SELECT 3, NULL
    UNION
    SELECT 4, NULL
    UNION
    SELECT 5, NULL
    UNION
    SELECT 6, NULL
)
SELECT
    DENSE_RANK() OVER (PARTITION BY CanBeNull ORDER BY CASE WHEN CanBeNull IS NOT NULL THEN value END ASC) as RowNumber
    ,Value
    ,CanBeNull
FROM sampledata
Run Code Online (Sandbox Code Playgroud)