SQL中的无损/公平共享部门

时间:2015-11-09 03:28:54

标签: sql postgresql integer-division

我有几种情况需要使用PostgreSQL将整数除以另一个整数并完成三件事:

  1. 所有结果数字都是整数
  2. 让所有结果数字的总和等于原始数字(无损方法)
  3. 使用"公平份额"传播,以避免曲棍球棒"将余数倾倒到最后一桶中的效果
  4. 我现在要处理的具体问题是我每月都有这样的数量:

    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周。它确实无关紧要,但如果您想知道如何处理低值,这就是我所拥有的心。

1 个答案:

答案 0 :(得分:1)

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

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