T-SQL为啥除以零不会在order by子句中产生错误

Posted

技术标签:

【中文标题】T-SQL为啥除以零不会在order by子句中产生错误【英文标题】:T-SQL Why division by zero does not generated error in the order by clauseT-SQL为什么除以零不会在order by子句中产生错误 【发布时间】:2013-03-29 16:02:40 【问题描述】:

我在一个程序中发现了一些我最初认为不正确的语句,但经过测试后,我确信它们运行良好。我无法理解的是方式。

我有一个简单的表,其中包含记录 idptsptsOf 列:

DECLARE @DataSource TABLE
(
     RecordID TINYINT
    ,Pts   INT
    ,PtsOf INT
)

INSERT INTO @DataSource
VALUES (1,5,5)
      ,(1,7,8)
      ,(1,3,5)
      ,(2,5,0)

我需要的是使用以下公式计算每条记录的总分:

SUM(pts)/SUM(ptsOf) * 100

因此,上面的语句将产生以下错误,因为对于最后一条记录,我将有 5/0

消息 8134,级别 16,状态 1,行 21 遇到除以零错误。

但我发现的语句仅在 select 子句中检查除以零,而在 order by 子句中没有:

 SELECT  RecordID
         ,CAST(CAST(SUM(Pts) AS decimal) / CASE SUM(PtsOf) WHEN 0 THEN NULL ELSE SUM(PtsOf) END * 100 AS decimal(18, 0))
 FROM @DataSource
 GROUP BY RecordID
 ORDER BY RecordID,  CAST(CAST(SUM(Pts) AS decimal) / SUM(PtsOf) * 100 AS decimal(18, 0)) ASC

为什么order by类中的计算没有产生错误?

以下是完整示例:

SET NOCOUNT ON
GO

    DECLARE @DataSource TABLE
    (
         RecordID TINYINT
        ,Pts   INT
        ,PtsOf INT
    )

    INSERT INTO @DataSource
    VALUES (1,5,5)
          ,(1,7,8)
          ,(1,3,5)
          ,(2,5,0)

     SELECT  RecordID
             ,CAST(CAST(SUM(Pts) AS decimal) / CASE SUM(PtsOf) WHEN 0 THEN NULL ELSE SUM(PtsOf) END * 100 AS decimal(18, 0))
     FROM @DataSource
     GROUP BY RecordID
     ORDER BY RecordID,  CAST(CAST(SUM(Pts) AS decimal) / SUM(PtsOf) * 100 AS decimal(18, 0)) ASC

SET NOCOUNT OFF
GO

【问题讨论】:

计算标量中的表达式不一定只计算一次。有时它们会被多次评估。如果实际上不需要结果,则其他时候根本不需要。 See this article for more about this @MartinSmith 非常感谢这篇文章。我现在确信在我的案例中“计算标量”没有被执行,因为在聚合表达式之后添加 pts/ptsOf 列会产生错误。 如果您尝试ORDER BY RecordID, (CRYPT_GEN_RANDOM(1) *0) + CAST(CAST(SUM(Pts) AS decimal) / SUM(PtsOf) * 100 AS decimal(18, 0)),您会看到评估不再延迟,并且按照该文章中的示例 4 引发错误。 【参考方案1】:

我相信ORDER BY 中的第二个子句在这种特殊情况下会被忽略。毕竟,如果你这样做:

DECLARE @t TABLE(i INT PRIMARY KEY, x UNIQUEIDENTIFIER);

INSERT @t VALUES(1,NEWID()),(2,NEWID()),(3,NEWID()),(4,NEWID());

SELECT i, x FROM @t ORDER BY i, x;

x 不在ORDER BY 中考虑,为什么会这样呢? ORDER BY 子句中的第一个实体已经规定了顺序,第二个子句不能更改它。由于您按RecordID 进行分组,SQL Server 足够聪明地意识到ORDER BY 中的第一个元素是唯一的,因此不需要考虑第二个元素。我无法证明这一点,当第二个元素实际上对 SQL Server 更清楚时,我可以通过使用常量使其失败,例如:

ORDER BY RecordID, CONVERT(1/0);

但是当 SQL Server 不容易知道列的输出,并且无论如何它不能对输出做任何有用的事情时,它会做正确的事情(恕我直言)并丢弃表达式而不完全评估它并导致运行时错误。如果您不首先按保证唯一的列排序,也可以返回错误:

ORDER BY CAST(CAST(SUM(Pts) AS decimal) / SUM(PtsOf) * 100 AS decimal(18, 0));

【讨论】:

感谢您的回答。确实,如果在聚合函数之后添加 pts/ptsOf 列,则会生成错误。因此,在我的示例中,只是因为不必要而被忽略。

以上是关于T-SQL为啥除以零不会在order by子句中产生错误的主要内容,如果未能解决你的问题,请参考以下文章

为啥 ORDER BY 子句中的绑定参数不对结果进行排序?

SQL语句中,为啥where子句不能使用列别名,而order by却可以?

为啥在mysql中第一个union两个子句的order by不起作用

如何在 select 语句中包含 PERCENTILE_CONT 列,而不会生成有关 ORDER BY 子句或聚合函数的错误?

允许 null 的 ORDER BY 列很慢。为啥?

如何避免 SQL 中的“除以零”错误?