SQL 服务器:避免舍入错误
SQL Server: Avoiding Rounding Errors
我需要 SQL 服务器中的浮点数学方面的帮助。下面给出问题的简化版本
DECLARE @TOTALCOST NUMERIC(18, 4) = 1.125;
WITH CTE AS (
SELECT item='Item1', volume=3.636
UNION
SELECT item='Item2', volume=14.946
UNION
SELECT item='Item3', volume=26.05
)
SELECT
item,
volume,
totVol = (SUM(volume) OVER (PARTITION BY NULL)),
proportion = (SUM(volume) OVER (PARTITION BY item)) / (SUM(volume) OVER (PARTITION BY NULL)),
costAllocation = (SUM(volume) OVER (PARTITION BY item)) * @TOTALCOST / (SUM(volume) OVER (PARTITION BY NULL))
FROM CTE
ITEM Volume totVol Proportion costAllocation
==========================================
Item1 3.636 44.632 0.081466 0.091649
Item2 14.946 44.632 0.334871 0.376730
Item3 26.050 44.632 0.583661 0.656619
基本上比例就是item1的体积占三个item总体积的比例,然后1.125除以这个比例。
那是针对 item1
比例 = 3.636 / 44.632
成本分配 = 3.636 * 1.125 / 44.632
我想要的是四舍五入的比例和costAllocation只保留小数点后三位,但是它们相加正好分别是1和1.125。
我曾尝试将 Round() 函数置于各种排列中,但不知何故,我无法准确计算出如何按比例划分,以便它恰好加起来为 1 和 1.125。我不介意一个数字的最后一位小数是否有错误。比如1可以平均分成3份=0.333___每份,所以即使我们用普通的舍入函数分到3份还是会显示为0.333,3次求和也只会是0.999
所以我可以将比例报告为 0.333、0.333 和 0.334,这样它加起来正好是 1。
您要处理的不是浮点数数学,而是由于四舍五入而缺乏小数精度。
我实施的一个解决方案(主要是在报告中)是做类似下面的事情,在其中计算由于舍入而产生的差异并将其添加到一行(通常是最大值以最小化影响)。
declare @TOTALCOST numeric(18, 4) = 1.125;
with CTE as (
select item='Item1', volume=3.636
union
select item='Item2', volume=14.946
union
select item='Item3', volume=26.05
), cte2 as (
select
item,
volume,
totVol = (Sum(volume) over ()),
proportion = Round((Sum(volume) over (partition by item)) / (Sum(volume) over ()),3),
costAllocation = Round((Sum(volume) over (partition by item)) * @TOTALCOST / (Sum(volume) over ()),3),
Row_Number() over(order by volume desc) rn
from CTE
)
select item, volume, totVol, proportion,
costAllocation + case when rn=1 then @TOTALCOST-Sum(costAllocation) over() else 0 end costAllocation
from cte2
我需要 SQL 服务器中的浮点数学方面的帮助。下面给出问题的简化版本
DECLARE @TOTALCOST NUMERIC(18, 4) = 1.125;
WITH CTE AS (
SELECT item='Item1', volume=3.636
UNION
SELECT item='Item2', volume=14.946
UNION
SELECT item='Item3', volume=26.05
)
SELECT
item,
volume,
totVol = (SUM(volume) OVER (PARTITION BY NULL)),
proportion = (SUM(volume) OVER (PARTITION BY item)) / (SUM(volume) OVER (PARTITION BY NULL)),
costAllocation = (SUM(volume) OVER (PARTITION BY item)) * @TOTALCOST / (SUM(volume) OVER (PARTITION BY NULL))
FROM CTE
ITEM Volume totVol Proportion costAllocation
==========================================
Item1 3.636 44.632 0.081466 0.091649
Item2 14.946 44.632 0.334871 0.376730
Item3 26.050 44.632 0.583661 0.656619
基本上比例就是item1的体积占三个item总体积的比例,然后1.125除以这个比例。 那是针对 item1 比例 = 3.636 / 44.632 成本分配 = 3.636 * 1.125 / 44.632
我想要的是四舍五入的比例和costAllocation只保留小数点后三位,但是它们相加正好分别是1和1.125。
我曾尝试将 Round() 函数置于各种排列中,但不知何故,我无法准确计算出如何按比例划分,以便它恰好加起来为 1 和 1.125。我不介意一个数字的最后一位小数是否有错误。比如1可以平均分成3份=0.333___每份,所以即使我们用普通的舍入函数分到3份还是会显示为0.333,3次求和也只会是0.999
所以我可以将比例报告为 0.333、0.333 和 0.334,这样它加起来正好是 1。
您要处理的不是浮点数数学,而是由于四舍五入而缺乏小数精度。
我实施的一个解决方案(主要是在报告中)是做类似下面的事情,在其中计算由于舍入而产生的差异并将其添加到一行(通常是最大值以最小化影响)。
declare @TOTALCOST numeric(18, 4) = 1.125;
with CTE as (
select item='Item1', volume=3.636
union
select item='Item2', volume=14.946
union
select item='Item3', volume=26.05
), cte2 as (
select
item,
volume,
totVol = (Sum(volume) over ()),
proportion = Round((Sum(volume) over (partition by item)) / (Sum(volume) over ()),3),
costAllocation = Round((Sum(volume) over (partition by item)) * @TOTALCOST / (Sum(volume) over ()),3),
Row_Number() over(order by volume desc) rn
from CTE
)
select item, volume, totVol, proportion,
costAllocation + case when rn=1 then @TOTALCOST-Sum(costAllocation) over() else 0 end costAllocation
from cte2