我正在创建一些测试数据,这需要我计算一个百分比。
在我的谓词中,我排除了任何会导致被零除错误的记录,并且当我对该数据集运行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.
答案 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的副产品包含数百万条记录的表变量。