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

时间:2011-08-26 12:59:59

标签: sql sql-server tsql

在询问another question时,我发现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,这是一个常量,所以它不应该工作......但确实如此。这种方式是否一致,或者是微软提出的特殊情况?

可以通过玩this query来检查所有这些behvaiour。


更新:此限制不仅适用于OVER子句(尽管它们确实提供了不同的错误) - 它适用于自SQL Server 2005以来的所有ORDER BY子句。Here's a query也显示了常规SELECT ORDER BY条款的限制。

2 个答案:

答案 0 :(得分:4)

联机书籍表明“排序列可以包含表达式,但当数据库处于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 

答案 1 :(得分:0)

第一个示例中的评估永远不会改变。

您正在将常数与常量进行比较,该常数将不断产生常数。

1=1始终为TRUE 1=0始终为FALSE