在问另一个问题时,我发现SQL Server(在2005年和2008年同时发生)在处理CASE窗口函数子句中的语句时似乎有奇怪的不一致行为。以下代码给出了一个错误:
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,这是一个常量。也可能像预期的那样,此代码给出了相同的错误:
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提出的特殊情况行为?
@asc
可以通过使用此查询来检查所有这些行为。
更新: 此限制不仅适用于OVER子句(尽管它们确实会给出不同的错误)-它适用于ORDER BYSQL Server 2005之后的所有子句。 这是一个查询,其中也显示了带有常规SELECT的ORDER BY子句的限制。
OVER
ORDER BY
SELECT
联机丛书表明“排序列可以包含表达式,但是当数据库处于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