【问题标题】:Bucket Filling SQL query CTEBucket Filling SQL 查询 CTE
【发布时间】:2017-01-15 11:43:30
【问题描述】:

我想从给定的输入表中实现以下输出。

输入表(待填充的桶)

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

输入表(填充表)

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

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

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

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

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

谢谢

【问题讨论】:

  • 用您正在使用的数据库标记您的问题。
  • 你可以使用递归 CTE 来做到这一点,但这有点傻。家庭作业的气味。无论如何,您都需要做一些工作,试一试并告诉我们为什么递归 CTE 不起作用。
  • 我使用的是 SQL Server 2008。

标签: sql sql-server sql-server-2008 common-table-expression bucket


【解决方案1】:

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

对累积和使用 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 表达式替换。

【讨论】:

  • Gordon 这不只是给出“结果”并且要求输出显示“填充过程”。显然教授正在寻找递归 CTE。
  • Hogan 我已经尝试过递归 CTE,它适用于单个填充值。请查看链接stackoverflow.com/questions/39353612/bucket-filling-sql-server 我的问题是当有填充表时。我尝试遍历此 CTE,但内存不足。 @霍根
  • 谢谢 Gordan,但是我想要显示填充时间线的确切输出表。
  • @user2123852 - 你不应该循环 CTE - CTE 应该是结果(填充过程中的所有步骤)
  • 谢谢霍根。我可以在具有填充值的存储桶表上递归,但是如何在存储桶和填充表上递归。
【解决方案2】:

您可以像这样在 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 Server 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

【讨论】:

猜你喜欢
  • 2017-01-17
  • 2016-01-25
  • 2012-09-10
  • 2011-10-10
  • 2020-05-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多