代码之家  ›  专栏  ›  技术社区  ›  HoneyBadger

为什么这个窗口表达式不会导致被零除的错误?

  •  0
  • HoneyBadger  · 技术社区  · 7 年前

    this 解答编程难题;代码高尔夫。在这本书中,作者使用了这样的表达方式(尽管答案后来被编辑为使用不同的解决方案):

    row_number()over(order by 1/0)
    

    1/0 导致除零异常,但事实并非如此。

    当我在PPCG上问作者时,他们回答说:“因为1/0不是计算出来的。 where exists(select 1/0) where exists(select 1) 是有效语法,但是 row_number()over(order by 1) 不是。那么为什么这个表达式在 order by 没有计算?表达式的结果是整数,表达式中不允许使用整数 . SQL Server如何处理 订货人 那样的话?我想效果和 row_number()over(order by (SELECT NULL)) ,但如果我们给出一个表达式,我希望该表达式被求值。

    SELECT  ROW_NUMBER() OVER ( ORDER BY A.x )
    FROM    (
                SELECT  *
                ,       1 / 0 x
                FROM    master..spt_values
            ) A
    

    同样,不报告被零除的错误(当x列未被选中时,自然)。那么,当整数不是整数时,为什么允许这样做呢?

    0 回复  |  直到 7 年前
        1
  •  4
  •   Martin Smith    7 年前

    让我们再举几个例子。。。


    ROW_NUMBER() OVER (ORDER BY 2/1)
    

    索引为ORDER BY子句表达式。

    问题在于 2/1 2 在优化过程的早期,因此被视为 ROW_NUMBER() OVER (ORDER BY 2) 这是不允许的。


    ROW_NUMBER() OVER (ORDER BY LOG(1))
    

    窗口函数和函数的下一个值不支持 常量作为ORDER BY子句表达式。

    常量折叠再次开始——这一次结果不是整数索引,但SQL Server无论如何都不允许常量。


    ROW_NUMBER() OVER (ORDER BY LOG(-1))
    

    这在最新版本的SQL Server上成功—在旧版本(如SQL Server 2008)上,您将看到 An invalid floating point operation occurred. . 这一具体案例是在 CASE here . 编译时常量折叠破坏了 案例

    在这些错误情况下对常数折叠的抑制( LOG(-1) 和 1/0 )足以让它绕过给出上述错误消息的检查。但是,SQLServer仍然认识到表达式实际上是一个常量,可以在以后进行优化(因此您不会得到排序操作来根据该表达式的结果对行进行排序)。

    在简化阶段 ROW_NUMBER ORDER BY non_folded_const_expression ROW_NUMBER 和 non_folded_const_expression

        2
  •  6
  •   Damien_The_Unbeliever    7 年前

    不允许你使用整数 字面量 ORDER BY 但你是被允许的 返回整数-否则您将无法使用例如。 CASE 表达。

    所以你不被允许 OVER (ORDER BY 1) 但显然是允许的 OVER (ORDER BY 1/0)

    select name,object_id,ROW_NUMBER() OVER (ORDER BY 1/(object_id-3))
    from sys.objects
    

    所以,我把它归结为“优化器足够聪明,可以意识到这个表达式不依赖于任何行值,因此它对所有行都是相同的值,所以我们不需要计算它”。在理智的语言中,这会产生一个警告。

        3
  •  1
  •   Suraj Kumar zip    7 年前

    SQL Server将order by表达式值转换为常量,而不是实际计算表达式。

    你也可以检查一下。

    Select 'a' as [Test] order by 1/0
    
        4
  •  1
  •   Gordon Linoff    7 年前

    编译

    在执行阶段生成的被零除错误不会被忽略。

    我将第一个查询写成:

    row_number() over (order by (select null))
    

    为了 exists ,我用 select 1

    推荐文章