用于计算分配/净额的SQL查询

时间:2014-01-17 04:36:04

标签: sql oracle

这是我的源数据,

Group | Item | Capacity
-----------------------
1     | A    | 100 
1     | B    | 80 
1     | C    | 20 
2     | A    | 90 
2     | B    | 40 
2     | C    | 20 

以上数据显示了为每件商品消费“东西”的能力。 现在假设我为每个组分配了最多100个。我希望将这个“100”分发给每个组,直到项目的最大容量。所以我想要的输出是这样的:

Group | Item | Capacity | consumption
-------------------------------------
1     | A    | 100      | 100
1     | B    | 80       | 0
1     | C    | 20       | 0
2     | A    | 90       | 90
2     | B    | 40       | 10
2     | C    | 20       | 0

我的问题是如何在单个SQL查询中执行此操作(最好避免使用任何子查询结构)。请注意,每组中的项目数量不固定。

我正在尝试LAG()运行SUM(),但无法完全生成所需的输出......

select 
group, item, capacity,
sum (capacity) over (partition by group order by item range between UNBOUNDED PRECEDING AND CURRENT ROW) run_tot,
from table_name

4 个答案:

答案 0 :(得分:3)

没有仅使用分析SUM函数的子查询:

SQL> create table mytable (group_id,item,capacity)
  2  as
  3  select 1, 'A' , 100 from dual union all
  4  select 1, 'B' ,  80 from dual union all
  5  select 1, 'C' ,  20 from dual union all
  6  select 2, 'A' ,  90 from dual union all
  7  select 2, 'B' ,  40 from dual union all
  8  select 2, 'C' ,  20 from dual
  9  /

Table created.

SQL> select group_id
  2       , item
  3       , capacity
  4       , case
  5         when sum(capacity) over (partition by group_id order by item) > 100 then 100
  6         else sum(capacity) over (partition by group_id order by item)
  7         end -
  8         case
  9         when nvl(sum(capacity) over (partition by group_id order by item rows between unbounded preceding and 1 preceding),0) > 100 then 100
 10         else nvl(sum(capacity) over (partition by group_id order by item rows between unbounded preceding and 1 preceding),0)
 11         end consumption
 12    from mytable
 13  /

  GROUP_ID I   CAPACITY CONSUMPTION
---------- - ---------- -----------
         1 A        100         100
         1 B         80           0
         1 C         20           0
         2 A         90          90
         2 B         40          10
         2 C         20           0

6 rows selected.

答案 1 :(得分:2)

这是使用递归子查询因子分解的解决方案。这显然忽略了你避免使用子查询的偏好,但是在一次通过中这样做可能是不可能的。

在一次通过中执行此操作的唯一方法可能是使用MODEL,我不允许在午夜之后编码。也许有人在欧洲醒来可以搞清楚。

with ranked_items as
(
    --Rank the items.  row_number() should also randomly break ties.
    select group_id, item, capacity,
        row_number() over (partition by group_id order by item) consumer_rank
    from consumption
),
consumer(group_id, item, consumer_rank, capacity, consumption, left_over) as
(
    --Get the first item and distribute as much of the 100 as possible.  
    select
        group_id,
        item,
        consumer_rank,
        capacity,
        least(100, capacity) consumption,
        100 - least(100, capacity) left_over
    from ranked_items
    where consumer_rank = 1
    union all
    --Find the next row by the GROUP_ID and the artificial CONSUMER_ORDER_ID.
    --Distribute as much left-over from previous consumption as possible.
    select
        ranked_items.group_id,
        ranked_items.item,
        ranked_items.consumer_rank,
        ranked_items.capacity,
        least(left_over, ranked_items.capacity) consumption,
        left_over - least(left_over, ranked_items.capacity) left_over
    from ranked_items
    join consumer
        on ranked_items.group_id = consumer.group_id
        and ranked_items.consumer_rank = consumer.consumer_rank + 1
)
select group_id, item, capacity, consumption
from consumer
order by group_id, item;

示例数据:

create table consumption(group_id number, item varchar2(1), capacity number);

insert into consumption
select 1, 'A' , 100 from dual union all
select 1, 'B' ,  80 from dual union all
select 1, 'C' ,  20 from dual union all
select 2, 'A' ,  90 from dual union all
select 2, 'B' ,  40 from dual union all
select 2, 'C' ,  20 from dual;
commit;

答案 2 :(得分:1)

这是否按预期工作?

WITH t AS
    (SELECT GROUP_ID, item, capacity,
    SUM(capacity) OVER (PARTITION BY GROUP_ID ORDER BY item RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) sum_run,
    GREATEST(100-SUM(capacity) OVER (PARTITION BY GROUP_ID ORDER BY item RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 0) AS remain
    FROM table_name)
SELECT t.*,
    LEAST(sum_run,lag(remain, 1, 100) OVER (PARTITION BY GROUP_ID ORDER BY item)) AS run_tot
FROM t

答案 3 :(得分:0)

  select  group_id,item,capacity,(case when rn=1 then capacity else 0 end) consumption
  from 
  (select group_id,item,capacity,
  row_number() over (partition by group_id order by capacity desc) rn from mytable)