我在数字之间拆分0.00xxx浮点值时遇到问题。 以下是输入data的示例 0是1-3个浮点数的总和。 结果我希望看到四舍五入的数字而不会丢失1-3的总和:
在:
0 313.726
1 216.412
2 48.659
3 48.655
OUT:
0 313.73
1 216.41
2 48.66
3 48.66
它应该如何运作: 想法是将最低的休息(在我们的例子中,它是从值216.412的0.002)分成最高值。 0.001到48.659 = 48.66和0.001到48.655 = 48.656之后我们可以在不丢失数据的情况下舍入数字。
昨天坐在这个问题后,我找到了解决方案。我认为的查询应该是这样的。
select test.*,
sum(value - trunc(value, 2)) over (partition by case when id = 0 then 0 else 1 end) part,
row_number() over(partition by case when id = 0 then 0 else 1 end order by value - trunc(value, 2) desc) rn,
case when row_number() over(partition by case when id = 0 then 0 else 1 end order by value - trunc(value, 2) desc) / 100 <=
round(sum(value - trunc(value, 2)) over (partition by case when id = 0 then 0 else 1 end), 2) then trunc(value, 2) + 0.01 else trunc(value, 2) end result
from test;
但是对我来说,在获得结果时添加const值“0.01”是很奇怪的。
有任何改进此查询的建议吗?
答案 0 :(得分:1)
在呈现结果时,您可以使用round()sql函数。 Round()的第二个参数是您想要将数字四舍五入的有效位数。在测试表上发出此选择:
select id, round(value, 2) from test;
为您提供以下结果
0 313.73
1 216.41
2 48.66
3 48.65
通常,您可以使用存储的数字进行求和,然后使用round()函数来显示结果:这是一种使用完整有效数字进行求和然后使用round()函数进行呈现的方法最终结果:
select sum(value) from test where id != 0
给出结果:313.726
select round(sum(value), 2) from test where id != 0
给出结果:313.73
顺便说一句,请允许我两点观察:
1)你给id = 3的舍入让我感到困惑:48.654轮到48.65而不是48.66两位有效数字。我错过了什么吗?
2)严格来说,这个问题不是标签上的pl / sql问题。它完全属于sql领域。但是,pl / sql中也有一个round()函数,并且适用相同的原则。
答案 1 :(得分:1)
如果我告诉你,不想要使用舍入,因为舍入部分数字与舍入总数不匹配。
在这种情况下,应用简单的技巧。你使用round除了最后一个数字。最后一个分数计算为舍入和和到目前为止的舍入部分之间的差异(除了最后一个之外)。
您可以用以下分析功能表达这一点
WITH total AS
(SELECT id, value, ROUND(value,2) value_rounded FROM test WHERE id = 0
),
rounded AS
( SELECT id, value, ROUND(value,2) value_rounded FROM test WHERE id != 0
)
SELECT id, value_rounded FROM total
UNION ALL
SELECT id,
CASE
WHEN row_number() over (order by id) != COUNT(*) over ()
THEN
/* not the last row - regular result */
value_rounded
ELSE
/* last row - corrected result */
(select value_rounded from total) - SUM(value_rounded) over (order by id ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING)
END AS value
FROM rounded
ORDER BY id;
请注意,这是对最后一个数字的测试
row_number() over (order by id) != COUNT(*) over ()
这是从开始(UNBOUNDED PRECEDING)到最后一个(最后一个)的所有部分的总和
SUM(value_rounded) over (order by id ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
我将您的数据分成两个来源总计 - 一行包含总数和舍入部分。
<强>更新强>
在某些情况下,最后更正的数字显示与原始值的“丑陋”大差异, 因为一个圆角方向的差异高于另一个圆角方向的差异。
以下选择考虑了这一点并分配了各部分之间的差异。
下面的示例在很多0.05s
的例子中说明了这一点 WITH nums AS
(SELECT rownum id, 0.005 value FROM dual connect by level <= 5
),
rounded AS
( SELECT id, value, ROUND(value,2) value_rounded FROM nums
),
with_diff as
(SELECT id, value, value_rounded,
-- difference so far - between the exact SUM and SUM of rounded parts
-- cut to two decimal points
floor(100* (
sum(value) over (order by id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) -
sum(value_rounded) over (order by id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)))
/ 100 diff_so_far
FROM rounded),
delta_diff as
(select id, value, value_rounded,DIFF_SO_FAR,
DIFF_SO_FAR - LAG(DIFF_SO_FAR,1,0) over (order by ID) as diff_delta
from with_diff)
SELECT id, value,
CASE
WHEN row_number() over (order by id) != COUNT(*) over ()
THEN
/* not the last row - take the rounded value and ... */
value_rounded +
/* ... add or subtract the delta difference */
diff_delta
ELSE
/* last row - corrected result */
round(sum(value) over(),2) - SUM(value_rounded + diff_delta) over (order by id ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING)
END AS value_rounded, diff_delta
FROM delta_diff
ORDER BY id;
ID VALUE VALUE_ROUNDED DIFF_DELTA
---------- ---------- ------------- ----------
1 ,005 0 -0,01
2 ,005 ,01 0
3 ,005 0 -0,01
4 ,005 ,01 0
5 ,005 ,01 -0,01
答案 2 :(得分:1)
select id, value,
case when id <> max(id) over () then round(value, 2)
else round(value, 2) - sum(round(value, 2)) over () +
round(first_value(value) over (order by id), 2) * 2
end val_rnd
from test
输出:
ID VALUE VAL_RND
------ ---------- ----------
0 313.726 313.73
1 216.413 216.41
2 48.659 48.66
3 48.654 48.66
以上查询有效,但它将所有差异移到最后一行。这不是“诚实的”,也许不是你追求的其他场景。
最“不诚实”的行为是可观察到的,具有大量值,均等于0.005
。
要进行全面分发,您需要:
sign()
,abs
),case when
),在一个查询中很难(但可以实现)。替代方案是一些PL / SQL过程或函数,它们可能更具可读性。
答案 3 :(得分:0)
基于以下规则的pragamtic解决方案:
1)检查舍入和与舍入部分之和之间的差异。
select round(sum(value),2) - sum(round(value,2)) from test where id != 0;
2)应用这种差异
e.g。如果你得到0.01,这意味着一个四舍五入的部分必须增加0.01
如果你得到-.02,则意味着两个四舍五入的部分必须减少0.01
下面的查询简单地纠正了最后N个部分:
with diff as (
select round(sum(value),2) - sum(round(value,2)) diff from test where id != 0
), diff_values as
(select sign(diff)*.01 diff_value, abs(100*diff) corr_cnt
from diff)
select id, round(value,2)
+ case when row_number() over (order by id desc) <= corr_cnt then diff_value else 0 end result
from test, diff_values where id != 0
order by id;
ID RESULT
---------- ----------
1 216,41
2 48,66
3 48,66
如果校正记录的数量远远高于2,请检查数据和舍入精度。