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

SQL Server在窗口函数子句中奇怪的不一致行为?,sql,sql-server,tsql,Sql,Sql Server,Tsql,在询问时,我发现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

在询问时,我发现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
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
错误是窗口函数不支持常量作为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 1=1 then SortColumn end asc,
            case when 1=0 then SortColumn end 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
,这是一个常量,因此它不应该工作。。。但确实如此。这是某种程度上的一致性,还是微软的特殊行为

这一切都可以通过玩游戏来制止



更新:此限制不仅适用于
OVER
子句(尽管它们会给出不同的错误)-它适用于自SQL Server 2005以来的所有
ORDER BY
子句。这还显示了常规的
SELECT
ORDER BY
子句的限制。

第一个示例中的计算永远不会更改。

你在比较一个常数和一个常数,这个常数会不断地产生一个常数

1=1
将始终为
TRUE


1=0
将始终为
FALSE

联机丛书指出“排序列可以包含表达式,但当数据库处于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=1
不也是一个常数吗?它的计算结果总是
TRUE
@Crack Yes,但它是
case
语句中的一个常量,所以我不明白为什么会有区别?这似乎是在说,“如果语句的计算结果(或不是)为NULL,那么它是不允许的。如果它的计算结果可能为NULL,那么它是允许的。”这没有意义。。。为什么允许后者?语法如何更有效?可以按常量排序。使用
orderby(选择NULL)
orderby@@SPID
您是否尝试按导致窗口函数错误的表达式实际排序?它们也会按正确的顺序产生错误:“在ORDER BY列表的位置n中遇到一个常量表达式”。因此,无论它在窗口函数中使用,它都会按顺序一致地工作和中断。@Andriy-。这里有一个非常相似的讨论,同样的问题也适用于这里,就像我之前问的@Crack;你是说规则是“如果语句的计算结果(或不是)为NULL,那么它是不允许的。如果它的计算结果可能为NULL,那么它是允许的。”这没有意义。。。为什么允许后者?一个语法怎么更有效呢?规则是“如果有一个常量求值,它就不可能出现在
OVER
子句中”,我怀疑。对我来说,这似乎是一个奇怪的规则。如果SQL Server可以处理非常量求值,那么处理常量求值难道不应该更容易吗?@Jez-当
ORDER BY
对于窗口函数的工作至关重要时,就不应该了。如果你能看到
(按“废话”排序)
,那就毫无意义了。SQL Server非常擅长于不让您执行会产生意外结果的操作,这与其他操作不同RDBMS@JNK-有时您只需要一个序列,而不关心排序,使用常量可以避免不必要的排序。这看起来像是对限制的准确描述。但是,为什么要实施限制是另一回事。。。