繁体   English   中英

SQL中的无损/公平份额部门

[英]Lossless / Fair Share Division in SQL

在几种情况下,我希望使用PostgreSQL能够将一个整数除以另一个整数并完成三件事:

  1. 让所有结果数字均为整数
  2. 使所有结果数字的总和等于原始数字(无损方法)
  3. 采用“公平份额”价差,以避免将剩余物倾倒到最后一个桶中的“曲棍球杆”效应

我现在要处理的特定问题是每月有这样的数量:

Month      Part       Qty
---------  ---------  -----
1/1/2016   ABC        10
2/1/2016   ABC         9

就我成功而言,这已经达到了我的前两个目标,但没有达到我的第三个目标:

with weekly_buckets as (
  select generate_series (0, 3) as week_number
)
select
  p.month_date + 7 * w.week_number as week_date,
  p.part_number,
  case w.week_number
    when 0 then p.qty / 4
    when 1 then p.qty / 4
    when 2 then p.qty / 4
    when 3 then p.qty - 3 * (p.qty / 4)
  end as qty
from
  part_demand p
  cross join weekly_buckets w

导致:

Week       Part       Qty
---------  ---------  -----
1/1/2016   ABC        2
1/8/2016   ABC        2
1/15/2016  ABC        2
1/22/2016  ABC        4

因此,最后一周的曲棍球棒效应为4。 我可以用天花板代替地板,但是在3、3、3、1时情况会更糟。

理想情况下,价差看起来像2、3、2、3或3、2、3、2。 这就是我所说的公平份额利差。 如果在数学上有一个更正确的术语,请赐教。

作为参考,这些数量代表每月的预测,我们正在尝试确定实际订单是否在预期目标之前,之后或之后。

另外,如果我能解决这个问题,我可以利用逻辑来做同样的事情来为年度数量创建月度存储桶。

作为最后一个例子,如果我看到这样的话:

Month      Part       Qty
---------  ---------  -----
1/1/2016   ABC        1

理想情况下,只有一个非零值的星期是第2周或第3周。这确实没有关系,但是如果您想知道将如何处理低值,这就是我的初衷。

您可以使用窗口函数来分配值。 这是您的第一个示例:

with b as (
      select generate_series(1, 4) as i, 10 as amt
     ),
     bb as (
      select b.*,
             count(*) over () as numbuckets,
             row_number() over (order by i) as rn,
         amt % (count(*) over () ) as remainder
      from b
     )
select bb.*,
       (amt / numbuckets +
        (case when rn <= remainder then 1 else 0 end)
       ) as partitioned
from bb;

这个想法是为每个存储桶分配amt / numbuckets 然后使用row_number()将剩余部分分布到各个存储桶中。 如果您希望这些值随机散布,请使用order by random()定义rn

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM