SQL四舍五入百分比,使总和100%-1/3为0.34、0.33、0.33
我目前正试图用百分比列拆分一个值。但由于大多数百分比值是1/3,我无法得到一个100%的值,该值有两个小数点。例如:SQL四舍五入百分比,使总和100%-1/3为0.34、0.33、0.33,sql,teradata,rounding,Sql,Teradata,Rounding,我目前正试图用百分比列拆分一个值。但由于大多数百分比值是1/3,我无法得到一个100%的值,该值有两个小数点。例如: Product Supplier percentage totalvalue customer_split decimal(15,14) (decimal(18,2) decimal(18,2) -------- -------- ------------
Product Supplier percentage totalvalue customer_split
decimal(15,14) (decimal(18,2) decimal(18,2)
-------- -------- ------------ --------------- ---------------
Product1 Supplier1 0.33 10.00 3.33
Product1 Supplier2 0.33 10.00 3.33
Product1 Supplier3 0.33 10.00 3.33
因此,在这里,我们在“值”列中缺少0.01,供应商希望将此缺少的0.01值与任意一个供应商进行随机比较。我一直试图在两组带有临时表的SQLs中实现这一点,但是有什么简单的方法可以做到这一点。如果可能的话,如何在上述行之一的百分比列中获得0.34?0.01是可以忽略的值,但当值列为100000000时,它是重要的。运行此命令,它将给出如何解决问题的想法。 我创建了一个名为orders的表,其ID很容易理解:
create table orders(
customerID int)
insert into orders values(1)
go 3
insert into orders values(2)
go 3
insert into orders values(3)
go 3
这些值代表您拥有的33%
1 33.33
2 33.33
3 33.33
现在:
这段代码将基本上计算百分比和最大ID的顺序,然后获得从100到百分比和的差值,并将其添加到最大ID的顺序(您的随机顺序)
听起来你在做某种“分配”。这是一个常见的问题,当您试图将某些内容从较高的粒度分配到较低的粒度时,您需要能够正确地重新聚合到总值 当处理较大的分数时,这将成为一个更大的问题 例如,如果我尝试将总值(比如55.30美元)除以8,那么八个桶中的每一个都会得到一个十进制值6.9125美元。我是不是应该把一个四舍五入到6.92美元,把其余的四舍五入到6.91美元?如果我这样做,我会损失一分钱。我得把一个四舍五入到6.93美元,其他的四舍五入到6.91美元。当您添加更多要除以的桶时,情况会变得更糟 此外,当您开始四舍五入时,您会引入诸如“33.339应该四舍五入到33.34还是33.33?” 如果您的业务逻辑是这样的,您只想获取可能存在的超过2个有效数字的余数,并将其“随机”添加到一个美元值中,这样您就不会损失任何美分,@Diego的做法是正确的 在纯SQL中实现这一点有点困难。首先,你的百分比不是1/3,而是0.33,这将产生9.9的总价值,而不是10。我会将其存储为比率或高精度十进制字段(.33333) 您的计算似乎是基于每个供应商的产品总数的平均分布。如果是,则删除百分比,而只在表中存储每个供应商的项目计数可能是有利的
如果还可以存储一个标志,指示应用剩余值的行,则可以基于该标志而不是随机分配。使用窗口聚合函数,这应该是一项简单的任务。您可能已经在计算
客户分割时使用了它们:
totalvalue / COUNT(*) OVER (PARTITION BY Product) as customer_split
现在将客户_分割相加,如果总价值存在差异,则将其添加(或减去)到一个随机行
SELECT
Product
,Supplier
,totalvalue
,customer_split
+ CASE
WHEN COUNT(*)
OVER (PARTITION BY Product
ROWS UNBOUNDED PRECEDING) = 1 -- get a random row, using row_number/order you might define a specific row
THEN totalvalue - SUM(customer_split)
OVER (PARTITION BY Product)
ELSE 0
END
FROM
(
SELECT
Product
,Supplier
,totalvalue
,totalvalue / COUNT(*) OVER (PARTITION BY Product) AS customer_split
FROM dropme
) AS dt
经过多次试验,我想我找到了更好的解决办法
创意
根据您的条件获取全部计数(计数(*)
获取行号()
检查(行数()值<计数(*))
然后选择舍入(当前百分比,2)
其他的
得到所有其他百分比的总和(四舍五入)并从100中减去它
此步骤每次都将选择当前百分比,但最后一步除外
100-所有其他百分比的总和
这是我代码的一部分
Select your_cols
,(Select count(*) from [tbl_Partner_Entity] pa_et where [E_ID] =@E_ID)
AS cnt_all
,(ROW_NUMBER() over ( order by pe.p_id)) as row_num
,Case when (
(ROW_NUMBER() over ( order by pe.p_id)) <
(Select count(*) from [tbl_Partner_Entity] pa_et where [E_ID] =@E_ID))
then round(([partnership_partners_perc]*100),2)
else
100-
((select sum(round(([partnership_partners_perc]*100),2)) FROM [dbo].
[tbl_Partner_Entity] PEE where [E_ID] =@E_ID and pee.P_ID != pe.P_ID))
end AS [partnership_partners_perc_Last]
FROM [dbo].[tbl_Partner_Entity] PE
where [E_ID] =@E_ID
选择您的
,(从[tbl_Partner_Entity]页面选择计数(*),其中[E_ID]=@E_ID)
作为所有人
,(按pe.p_id排序)上的行数()作为行数
,例如(
(按pe.p\U id排序)上方的行号()
(从[tbl_Partner_Entity]页面选择计数(*),其中[E_ID]=@E_ID))
然后是第二轮(([partnership_partners_perc]*100),第二轮)
其他的
100-
(从[dbo]中选择总和(四舍五入([partnership_partners_perc]*100),2)。
[tbl_Partner_Entity]PEE,其中[E_ID]=@E_ID和PEE.P_ID!=pe.P_ID))
终止为[合伙企业\合伙人\最后一个]
来自[dbo].[tbl\U合作伙伴\U实体]PE
其中[E_ID]=@E_ID
您的百分比有14位小数,为什么不在百分比
列中输入0.33333
?如果我说“您只需更新供应商设置百分比=0.34,其中…”然后你可能会明白,仅仅通过阅读这个问题,理解你真正需要什么是多么困难……看看马丁·福勒的“数量”模式。特别是,看看关于资金分配的讨论。示例中的除法返回一个值数组,而不是单个值。值的数组加起来就是总数。我发现这很简单。我知道我们可以在已经计算的百分比的基础上更新/使用第二组sql,这就是我目前正在计算的。。但在我们计算百分比的同一个sql中,这不是有一个简单的逻辑吗?两组sql可以轻松地完成这项任务,这就是我目前正在做的。但我正在寻找一个单步流程,在该流程中,丢失的数据应该针对任何随机供应商自动分配,性能影响较小,因为它需要在Teradata上每小时运行数十亿行。这看起来几乎与我预期的一样。非常感谢你。现在,我必须应用这个逻辑和调优,以便在Teradata上实现这一点。非常感谢。如果您的主要索引在产品密钥上,您应该会获得相当好的性能,因为每个放大器都应该处理每个计算。是的。但我在这里给出的示例与我的场景并不完全相同。我已经有了一套SQL,它可以用更复杂的连接完成大部分的拆分工作。上面提供的示例是为了使其易于理解。非常感谢你,N West。
totalvalue / COUNT(*) OVER (PARTITION BY Product) as customer_split
SELECT
Product
,Supplier
,totalvalue
,customer_split
+ CASE
WHEN COUNT(*)
OVER (PARTITION BY Product
ROWS UNBOUNDED PRECEDING) = 1 -- get a random row, using row_number/order you might define a specific row
THEN totalvalue - SUM(customer_split)
OVER (PARTITION BY Product)
ELSE 0
END
FROM
(
SELECT
Product
,Supplier
,totalvalue
,totalvalue / COUNT(*) OVER (PARTITION BY Product) AS customer_split
FROM dropme
) AS dt
Select your_cols
,(Select count(*) from [tbl_Partner_Entity] pa_et where [E_ID] =@E_ID)
AS cnt_all
,(ROW_NUMBER() over ( order by pe.p_id)) as row_num
,Case when (
(ROW_NUMBER() over ( order by pe.p_id)) <
(Select count(*) from [tbl_Partner_Entity] pa_et where [E_ID] =@E_ID))
then round(([partnership_partners_perc]*100),2)
else
100-
((select sum(round(([partnership_partners_perc]*100),2)) FROM [dbo].
[tbl_Partner_Entity] PEE where [E_ID] =@E_ID and pee.P_ID != pe.P_ID))
end AS [partnership_partners_perc_Last]
FROM [dbo].[tbl_Partner_Entity] PE
where [E_ID] =@E_ID