小编典典

窗口函数子句中来自SQL Server的奇怪不一致行为?

sql

在问另一个问题时,我发现SQL
Server(在2005年和2008年同时发生)在处理CASE窗口函数子句中的语句时似乎有奇怪的不一致行为。以下代码给出了一个错误:

declare @t table (SortColumn int)
insert @t values (1), (2), (3)
declare @asc bit
set @asc = 0

select  row_number() over (order by
            case when 1=1 then SortColumn end asc,
            case when 1=0 then SortColumn end desc) RowNumber
,       *
from    @t

错误是 窗口函数不支持将常量用作ORDER BY子句表达式。
我认为这是因为该case语句的计算结果可能为NULL,这是一个常量。也可能像预期的那样,此代码给出了相同的错误:

declare @t table (SortColumn int)
insert @t values (1), (2), (3)
declare @asc bit
set @asc = 0

select  row_number() over (order by
            NULL asc,
            NULL desc) RowNumber
,       *
from    @t

…大概是出于同样的原因。但是,此代码不会给出错误:

declare @t table (SortColumn int)
insert @t values (1), (2), (3)
declare @asc bit
set @asc = 0

select  row_number() over (order by
            case when @asc=1 then SortColumn end asc,
            case when @asc=0 then SortColumn end desc) RowNumber
,       *
from    @t

与第一个代码块的唯一区别是,我已将case语句的条件操作数之一移至变量@asc。现在可以正常工作了。为什么呢?该case声明可能还是评估到NULL,这是一个常数,因此它不应该工作…但它确实。这是某种程度的一致性,还是Microsoft提出的特殊情况行为?

可以通过使用此查询来检查所有这些行为。


更新: 此限制不仅适用于OVER子句(尽管它们确实会给出不同的错误)-它适用于ORDER BYSQL Server 2005之后的所有子句。
这是一个查询,其中也显示了带有常规SELECTORDER BY子句的限制。


阅读 232

收藏
2021-03-23

共1个答案

小编典典

联机丛书表明“排序列可以包含表达式,但是当数据库处于SQL Server(90)兼容模式时,表达式无法解析为常量。” 但是,它没有定义“常量”。

通过考虑和实验,可以清楚地看出这意味着可以在编译时成功计算出其常量值的表达式。

/*Works - Constant at run time but SQL Server doesn't do variable sniffing*/
DECLARE @Foo int
SELECT ROW_NUMBER() OVER (ORDER BY @Foo) 
FROM master..spt_values

/*Works - Constant folding not done for divide by zero*/
SELECT ROW_NUMBER() OVER (ORDER BY $/0) 
FROM master..spt_values

/*Fails - Windowed functions do not support 
   constants as ORDER BY clause expressions.*/
SELECT ROW_NUMBER() OVER (ORDER BY $/1) 
FROM master..spt_values
2021-03-23