当条件被明确排除时,除以零错误

时间:2019-02-23 18:13:35

标签: tsql ssms

我正在创建一些测试数据,这需要我计算一个百分比。

在我的谓词中,我排除了任何会导致被零除错误的记录,并且当我对该数据集运行SQL查询时,一切运行正常。

已生成的记录总数(所有组合):92,345,408

除以零实例划分的记录总数:92,141,104

当我添加“符合用例1的条件”条件时,查询仍将执行而没有错误。但是,当我还将“用例2”添加到谓词中时,会遇到被零除的错误。我不了解这种情况的发生,因为我排除了这种情况:

WHERE CAST(m1.MoneyValue1 AS FLOAT) - CAST(m2.MoneyValue2 AS FLOAT) != 0

下面是我的代码来创建3个不同的美元值列(DECIMAL(18,2)),然后使用“交叉应用”来获取所有可能的组合。

DECLARE @Money1 TABLE
    (
    ID INT IDENTITY (1,1) NOT NULL,
    MoneyValue1 DECIMAL (18,2) NOT NULL
    )

DECLARE @Money2 TABLE
    (
    ID INT IDENTITY (1,1) NOT NULL,
    MoneyValue2 DECIMAL (18,2) NOT NULL
    )

DECLARE @Money3 TABLE
    (
    ID INT IDENTITY (1,1) NOT NULL,
    MoneyValue3 DECIMAL (18,2) NOT NULL
    )


DECLARE @stop DECIMAL(18,2) = 2000.00   --  capping the maximum test value at $2000.00
DECLARE @interval FLOAT = 4.43  --  adding a random dollar amount to create variability and several test values

DECLARE @MoneyValue DECIMAL (18,2) = 0  --  for my test, I don't care about negative dollar amounts

WHILE @MoneyValue < @stop
    BEGIN
        INSERT INTO @Money1
            (
            MoneyValue1
            )
            SELECT CAST(@MoneyValue AS DECIMAL(18,2))

            SET @MoneyValue = CAST(@MoneyValue AS FLOAT) + CAST(@interval AS FLOAT)

    END

    INSERT INTO @Money2 --  use the same values generated by the statement above for my second Money column
        (
        MoneyValue2
        )
        SELECT
            CAST(MoneyValue1 AS DECIMAL(18,2))
        FROM @Money1

    INSERT INTO @Money3 --  use the same values generated by the statement above for my second Money column
        (
        MoneyValue3
        )
        SELECT
            CAST(MoneyValue1 AS DECIMAL(18,2))
        FROM @Money1

接下来,我想创建10个随机数据样本; Calc列将显示用例1的值(请参见以下导致错误的谓词示例中的谓词)。

SELECT TOP 10
    m1.MoneyValue1 AS TotalPmt,
    m2.MoneyValue2 AS TotalPmtChange,
    m3.MoneyValue3 AS PmtChangeAmount
    ,CAST(m2.MoneyValue2 AS FLOAT) / (CAST(m1.MoneyValue1 - m2.MoneyValue2 AS FLOAT)) AS Calc
FROM @Money1 AS m1
CROSS APPLY @Money2 AS m2
CROSS APPLY @Money3 AS m3
WHERE CAST(m1.MoneyValue1 AS FLOAT) - CAST(m2.MoneyValue2 AS FLOAT) != 0    --  exclude the possibility of a divide by zero error
ORDER BY NEWID()

如果我将谓词更改为现在也只包含用例1,则查询将正确执行。

WHERE CAST(m1.MoneyValue1 AS FLOAT) - CAST(m2.MoneyValue2 AS FLOAT) != 0    --  exclude the possibility of a divide by zero error
    AND CAST(m2.MoneyValue2 AS FLOAT) / (CAST(m1.MoneyValue1 - m2.MoneyValue2 AS FLOAT)) > .1   --  qualify for Use Case 1
ORDER BY NEWID()

但是,如果我更改谓词以同时包含用例1和用例2条件,我现在将得到除以零的错误!

WHERE CAST(m1.MoneyValue1 AS FLOAT) - CAST(m2.MoneyValue2 AS FLOAT) != 0    --  exclude the possibility of a divide by zero error
    AND CAST(m2.MoneyValue2 AS FLOAT) / (CAST(m1.MoneyValue1 - m2.MoneyValue2 AS FLOAT)) > .1   --  qualify for Use Case 1
    AND CAST(m3.MoneyValue3 AS FLOAT) / (CAST(m1.MoneyValue1 - m2.MoneyValue2 AS FLOAT)) > .1   --  qualify for Use Case 2

来自SSMS的消息:

(452 row(s) affected)
Msg 8134, Level 16, State 1, Line 58
Divide by zero error encountered.

1 个答案:

答案 0 :(得分:1)

虽然我不一定要指出故障的机理,但是我可以说,一旦将数据移到物理表中,除零错误就会停止。

一篇支持使用表变量作为原因的文章: When should I use a table variable vs temporary table in sql server?

也许无法在表变量上创建和运行统计信息导致引擎与除零记录冲突。另一种可能是SQL Server无法正确查看表变量的基数,即,估计要从表变量输出的一条记录。

我发现上面的链接很有趣,其中包含对该链接的引用:What's the difference between a temp table and table variable in SQL Server?

  

无列统计

     

具有更准确的表基数并不意味着估计的基数   行计数将更加准确(除非进行操作   在表格中的所有行上)。 SQL Server不维护列   表变量的统计信息完全可以,因此可以依靠猜测   根据比较谓词(例如,该表的10%   对于非唯一列,返回=;对于>,返回30%   比较)。相反,为#temp保留列统计信息   表。

无论出于何种原因,我发现的解决方案都可以回溯到最初问题的原因(当我的谓词明确排除了零除的可能性时遇到零除错误)是使用a的副产品包含数百万条记录的表变量。