【问题标题】:Curious inconsistent behaviour from SQL Server in windowed function clauses?SQL Server 在窗口函数子句中奇怪的不一致行为?
【发布时间】:2011-08-27 20:55:40
【问题描述】:

在询问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,这是一个常数,因此它不应该工作......但它确实有效。这在某种程度上是一致的,还是 Microsoft 提出的特殊情况行为?

所有这些行为都可以通过this query 来检查。


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

【问题讨论】:

  • 1=1 不也是一个常数吗?它总是评估为TRUE
  • @Crack 是的,但它在case 语句中是一个常数,所以我不明白为什么会有所不同?这似乎是在说,“如果该语句肯定评估(或不评估)为 NULL,则不允许。如果它可能评估为 NULL,则允许。”这没有意义......为什么允许后者?语法如何更有效?
  • 您可以按常数排序。使用ORDER BY (SELECT NULL)ORDER BY @@SPID
  • 您是否尝试对导致窗口函数错误的表达式进行实际排序?它们也会在正确的 ORDER BY 中产生错误:'在 ORDER BY 列表中遇到常量表达式,位置 n'。因此,无论它是否在窗口函数中使用,它都能在 ORDER BY 中始终如一地工作和中断。
  • @Andriy - This depends on compatibility mode。一个非常相似的讨论在这里dbaspot.com/sqlserver-programming/…

标签: sql sql-server tsql


【解决方案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 

【讨论】:

  • 这看起来像是对限制的准确描述。 为什么有限制是另一回事,不过...
【解决方案2】:

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

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

1=1 永远是TRUE
1=0 永远是FALSE

【讨论】:

  • 我之前问过@Crack 的问题在这里也适用;你是说规则是,“如果语句肯定评估(或不评估)为 NULL,则不允许。如果它可能评估为 NULL,则允许。”这没有意义......为什么允许后者?语法如何更有效?
  • 规则是“如果有一个持续的评估,它就不能在OVER 子句中”,我怀疑。
  • 对我来说似乎是一个奇怪的规则。如果 SQL Server 可以处理非常量求值,那么处理常量求值是否应该更容易(如果有的话)?
  • @Jez - 当ORDER BY 对窗口函数的工作至关重要时,则不然。如果您能看到OVER(ORDER BY 'blah'),那将毫无意义。 SQL Server 非常擅长不让您做会产生意外结果的事情,这与其他一些 RDBMS 不同
  • @JNK - 有时您只需要一个序列而不关心排序,使用常量可以避免不必要的排序。 One example here
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-03
  • 2014-10-22
  • 2013-06-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多