SQL Server在窗口函数子句中奇怪的不一致行为?
在询问时,我发现SQL Server(发生在2005年和2008年)在处理窗口函数子句中的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
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-有时您只需要一个序列,而不关心排序,使用常量可以避免不必要的排序。这看起来像是对限制的准确描述。但是,为什么要实施限制是另一回事。。。