Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在一个SQL请求中舍入一列,而不改变总的求和?_Sql_Sql Server_Algorithm_Sql Server 2008 - Fatal编程技术网

如何在一个SQL请求中舍入一列,而不改变总的求和?

如何在一个SQL请求中舍入一列,而不改变总的求和?,sql,sql-server,algorithm,sql-server-2008,Sql,Sql Server,Algorithm,Sql Server 2008,我有一个定义如下的表: create table #tbFoo (bar float) 我正在寻找一种方法,在不改变总和的情况下对列栏中包含的每个值进行四舍五入(已知总和为整数,或由于浮点数精度非常接近整数) 将每个值四舍五入到最接近的整数将不起作用(例如:1,5;1,5将四舍五入到1;1或2;2) 使用几个请求(例如存储原始和、舍入、计算新和,以及根据需要更新尽可能多的行以返回原始和)可以很容易地做到这一点,但这不是一个非常优雅的解决方案 有没有一种方法可以使用一个SQL请求来实现这一点

我有一个定义如下的表:

create table #tbFoo
(bar float)
我正在寻找一种方法,在不改变总和的情况下对列栏中包含的每个值进行四舍五入(已知总和为整数,或由于浮点数精度非常接近整数)

将每个值四舍五入到最接近的整数将不起作用(例如:1,5;1,5将四舍五入到1;1或2;2)

使用几个请求(例如存储原始和、舍入、计算新和,以及根据需要更新尽可能多的行以返回原始和)可以很容易地做到这一点,但这不是一个非常优雅的解决方案

有没有一种方法可以使用一个SQL请求来实现这一点


我使用的是SQL Server 2008,因此欢迎使用此特定供应商的解决方案



编辑:我正在寻找一个最小化旧值和新值之间差异的请求。换句话说,如果一个较大的值被向下舍入,则该值不应向上舍入,反之亦然。

更新:

请参阅我的博客文章中详细介绍的此解决方案:


您需要为每个值保留累积偏移量:

1.2   (1 + 0.0)  ~ 1    1   1.2   +0.2
1.2   (1 + 0.2)  ~ 1    2   2.4   +0.4
1.2   (1 + 0.4)  ~ 1    3   3.6   +0.6
1.2   (1 + 0.6)  ~ 2    5   4.8   -0.2
1.2   (1 - 0.2)  ~ 1    6   6.0   0.0
这在
MySQL
中很容易实现,但在
sqlserver
中,您必须编写游标或使用累积子选择(效率较低)


更新:

下面的查询选择值和向下舍入到最接近的较小整数之间的差值

这给了我们应该取整的数值(
N

然后,我们按分数部分对值进行排序(那些更接近其上限的先排序),并将第一个
N
向上舍入,其他值向下舍入

SELECT  value,
        FLOOR(value) + CASE WHEN ROW_NUMBER() OVER (ORDER BY value - FLOOR(value) DESC) <= cs THEN 1 ELSE 0 END AS nvalue
FROM    (
        SELECT  cs, value
        FROM    (
                SELECT  SUM(value) - SUM(FLOOR(value)) AS cs
                FROM    @mytable
                ) c
        CROSS JOIN
                @mytable
        ) q
选择值,

下限(值)+行数()超过时的情况(按值排序-下限(值)描述)首先获取舍入和与实际和之间的差值,以及记录数:

declare @Sum float, @RoundedSum float, @Cnt int

select @Sum = sum(bar), @RoundedSum = sum(round(bar)), @Cnt = count(*)
from #tbFoo
然后在四舍五入之前,将差值平均分布在所有值上:

declare @Offset float

set @Offset = (@Sum - @RoundedSum) / @Cnt

select bar = round(bar + @Offset)
from #tbFoo

如果您有一个n个值的列表,其中的元素仅精确到一个整数值(+-0.5)以内,那么这些元素的任何和都将有一个累积误差或+-(n*0.5)。如果列表中有6个元素,加起来应该是一个数字,那么最坏的情况是,如果只加上整数值,则会减少3个元素

如果你想办法将10.2表示为11,以使求和有效,那么你已经将该元素的精度从+-0.5更改为+-0.8,这在查看整数时是违反直觉的

一个可能的解决方案是只在显示期间(在输出中使用一些格式字符串)对数字进行四舍五入,而不是在检索阶段。每个数字将尽可能接近实际值,但总和也将更准确

示例:如果有3个值,每个值为1/3,显示为整数百分比,则应显示33、33和33。做任何其他事情都是为任何单个值创建大于+-0.5的误差范围。您的合计仍应显示为100%,因为这是可能的最佳值(与使用已四舍五入值的总和不同)

另外,请注意,通过使用浮点,您已经对精度引入了限制,因为您无法准确表示0.1。欲了解更多信息,请阅读

@Quassnoi:谢谢你的回答。使用您的解决方案,1.4/1.4/1.2将四舍五入为1/1/2,这是不公平的,因为1.4大于1.2。@Quassnoi:这正是我想要的。谢谢@在我的现实世界问题中,我有另一列'ListID',我希望每个ListID的总和保持不变。我已经使用GROUPBY子句修改了您的请求,它几乎可以工作,但是行号应用于我的整个请求,而不是GROUPBY请求。有什么我能做的吗?@Brann:你能发布一些示例数据和所需的结果集吗?
@Brann
:同时,尝试用
OVER(按值排序)
替换
OVER(按列表ID按值排序)
。这将按组返回行号。@guffa:如果我没有弄错的话:0.2/0.2/0.2/0.2/0.2,sum=1,roundedsum=0,offest=0.2,finalresulst=0/0/0/0,finalsum=0。@Brann:是的,这不会给出正确的结果。然而,平均分配偏差的原则是正确的。不过我会再给它一些…@Galghamon:是的,将10.2显示为11正是我想要实现的。这也是用户想要看到的。一个好问题。我把它放在我的博客帖子待办事项列表中。
declare @Offset float

set @Offset = (@Sum - @RoundedSum) / @Cnt

select bar = round(bar + @Offset)
from #tbFoo