桶填充 SQL 查询 CTE

Bucket Filling SQL query CTE

我想从给定的输入 tables 中获得以下输出。

输入Table(要填充的桶)

ID | FullCapacity | CurrentAmount    
---+--------------+--------------
B1 |     100      |     0    
B2 |      50      |     0
B3 |      70      |     0

输入Table(填充Table)

        ID | Filler            
        ---+-------
        F1 | 90              
        F2 | 70          
        F3 | 40    
        F4 | 20 

输出 table 应该有下面显示的填充过程。

ID | FullCapacity | CurrentAmount       
---+--------------+--------------
B1 |    100       |    90        
B2 |     50       |     0    
B3 |     70       |     0
---+--------------+--------------
B1 |    100       |   100        
B2 |     50       |    50
B3 |     70       |    10
---+--------------+--------------
B1 |    100       |   100      
B2 |     50       |    50    
B3 |     70       |    50
---+--------------+--------------
B1 |    100       |   100        
B2 |     50       |    50    
B3 |     70       |    70

我正在尝试从填充物到桶一个一个地填充这个。我们可以不使用光标来做到这一点吗?

请注意我们可以有多种类型的桶,例如红色桶、蓝色桶和红色填充物、蓝色填充物。红色填料去红色桶,蓝色填料去蓝色等等。

谢谢

您需要做的只是累加和一些连接。因此,您可以在没有光标的情况下执行此操作。这个想法是使用累积连接,然后根据范围将每个填充记录分配给一个或多个存储桶。

对累积和使用 ANSI 标准语法:

select b.*, f.id,
       (greatest(b.cumecap - b.fullcapacity, f.cumefiller - f.filler) - 
        least(b.cumecap, f.cumefiller)
       ) as amount_in_bucket
from (select b.*,
             sum(b.fullcapacity) over (order by id) as cumecap
      from buckets
     ) b join
     (select f.*,
             sum(f.filler) over (order by id) as cumefiller
      from filler f
     ) f
     on f.cumefiller - f.filler <= b.cumecap and
        f.cumefiller >= b.cumecap - b.fullcapacity;

这会生成每个桶的映射以及桶中每个填充物的数量。

注意:它使用了函数 greatest()least()。如果函数不可用,这些很容易被 case 表达式替换。

您可以在 SQL Server 2008 中这样做:

declare @Buckets table (ID char(2), FullCapacity int)
declare @Filler table (ID char(2), Filler int)

insert into @Buckets 
select 'B1', 100 union all
select 'B2', 50 union all
select 'B3', 70 

insert into @Filler 
select 'F1', 90 union all
select 'F2', 70 union all
select 'F3', 40 union all
select 'F4', 20


select 
    b.ID, 
    b.FullCapacity,
    case 
        when f.TotalFill < b.RunningTotalCapacity then 0
        when f.TotalFill > b.RunningTotalCapacity + b.FullCapacity then b.FullCapacity
        else f.TotalFill - b.RunningTotalCapacity
    end as CurrentAmount
from
(
    select      
    ID,
    Filler,
    (
        select sum(f2.Filler)
        from @Filler as f2
        where f2.ID <= f.ID
    ) as TotalFill
    from @Filler as f
) as f
cross join 
(
    select 
        ID,
        FullCapacity, 
        (
            select isnull(sum(b2.FullCapacity), 0)
            from @Buckets as b2
            where b2.ID < b.ID
        ) as RunningTotalCapacity
    from @Buckets as b
) as b
order by f.ID, b.ID

您可以像这样使用窗口函数来做到这一点:

SQL 服务器 2012+

    declare @Buckets table (ID char(2), FullCapacity int)
    declare @Filler table (ID char(2), Filler int)

    insert into @Buckets values
    ('B1', 100),
    ('B2', 50),
    ('B3', 70)

    insert into @Filler values
    ('F1', 90),
    ('F2', 70),
    ('F3', 40),
    ('F4', 20)

    ;with fillerCte as
    (
        select      
            ID,
            Filler,
            sum(Filler) over (order by ID) as TotalFill
        from @Filler
    ), 
    BucketCte as
    (
        select 
            ID,
            FullCapacity,
            sum(FullCapacity) over (order by ID) - FullCapacity as RunningTotalCapacity
        from @Buckets
    )
    select 
        b.ID, 
        b.FullCapacity,
        case 
            when f.TotalFill < b.RunningTotalCapacity then 0
            when f.TotalFill > b.RunningTotalCapacity + b.FullCapacity then b.FullCapacity
            else f.TotalFill - b.RunningTotalCapacity
        end as CurrentAmount
    from fillerCte as f
    cross join BucketCte as b
    order by f.ID, b.ID