将列的总和限制为另一列中的值

时间:2016-06-08 10:05:06

标签: sql sql-server

我在MS SQL中有这个表,看起来像这个

    ID           DATE              LIMIT     VALUE
    1      2016-01-01 00:00:00      10        5
    1      2016-02-01 00:00:00      10        3
    1      2016-03-01 00:00:00      10        1
    1      2016-04-01 00:00:00      10        3
    2      2016-01-01 00:00:00      15        16
    2      2016-02-01 00:00:00      15        5
    3      2016-01-01 00:00:00       8        1
    3      2016-02-01 00:00:00       8        2     
    3      2016-03-01 00:00:00       8        2
    3      2016-04-01 00:00:00       8        0
    3      2016-05-01 00:00:00       8        1

我想创建一个SELECT语句,它将LIMIT的VALUE加总。如下图所示

    ID           DATE              LIMIT     VALUE
    1      2016-01-01 00:00:00      10        5
    1      2016-02-01 00:00:00      10        3
    1      2016-03-01 00:00:00      10        1
    1      2016-04-01 00:00:00      10        1
    2      2016-01-01 00:00:00      15        15
    2      2016-02-01 00:00:00      15        0
    3      2016-01-01 00:00:00       8        1
    3      2016-02-01 00:00:00       8        2     
    3      2016-03-01 00:00:00       8        2
    3      2016-04-01 00:00:00       8        0
    3      2016-05-01 00:00:00       8        1

如您所见,第4,5行和第6行中的值已更改,因此ID,DATE和LIMIT的值的运行总和不会超过LIMIT。 (可以删除VALUE中为0的第6行。)

要明确的是,LIMIT适用于运行总计,基于DATE的排序(感谢Damien_the_unbeliever使其更清晰)

非常感谢任何帮助。谢谢!

3 个答案:

答案 0 :(得分:2)

这会产生所需的结果集,并且希望相当容易阅读并推理 1

declare @t table (ID int not null,[DATE] datetime not null, LIMIT int not null,
                  VALUE int not null)
insert into @t(ID,[DATE],LIMIT,VALUE) values
(1,'2016-01-01T00:00:00',10,5 ),
(1,'2016-02-01T00:00:00',10,3 ),
(1,'2016-03-01T00:00:00',10,1 ),
(1,'2016-04-01T00:00:00',10,3 ),
(2,'2016-01-01T00:00:00',15,16),
(2,'2016-02-01T00:00:00',15,5 ),
(3,'2016-01-01T00:00:00', 8,1 ),
(3,'2016-02-01T00:00:00', 8,2 ),
(3,'2016-03-01T00:00:00', 8,2 ),
(3,'2016-04-01T00:00:00', 8,0 ),
(3,'2016-05-01T00:00:00', 8,1 )

select ID,[DATE],LIMIT,
    CASE
        WHEN v2 > LIMIT THEN 0
        WHEN COALESCE(v2,0) + VALUE > LIMIT THEN LIMIT - COALESCE(v2,0)
        ELSE VALUE
    END as VALUE
from
    @t t
        cross apply
    (select SUM(VALUE) from @t v where v.ID = t.ID and v.[DATE] < t.[DATE]) v(v2)

结果:

ID          DATE                    LIMIT       VALUE
----------- ----------------------- ----------- -----------
1           2016-01-01 00:00:00.000 10          5
1           2016-02-01 00:00:00.000 10          3
1           2016-03-01 00:00:00.000 10          1
1           2016-04-01 00:00:00.000 10          1
2           2016-01-01 00:00:00.000 15          15
2           2016-02-01 00:00:00.000 15          0
3           2016-01-01 00:00:00.000 8           1
3           2016-02-01 00:00:00.000 8           2
3           2016-03-01 00:00:00.000 8           2
3           2016-04-01 00:00:00.000 8           0
3           2016-05-01 00:00:00.000 8           1

我们只是在这里使用CROSS APPLY来查找以前所有行的总值(基于DATE)并使用短CASE表达式来确定每行可能有三种情况 - 我们已经超过限制,我们是超过限制 2 的行,或者我们远低于限制。

1 还要注意我是如何将您的样本数据转换为比您的问题中的样本占用尽可能多/略少的空间的东西,但也具有 runnable的巨大好处生成示例数据的代码

2 如果集合的第一行超出限制(对于问题的ID 2行),此答案的原始版本将失败。此后,已针对此案例使用COALESCE进行了更正。

答案 1 :(得分:0)

您可能需要做的是

LEAST( YourTable.Limit, SUM( YourTable.Value ) ) as Value

和ID和日期的组,但没有足够的原始数据来显示完整的SQL。

答案 2 :(得分:0)

有点

WITH cte AS (
    SELECT *, rt=SUM(Value) OVER(PARTITION BY id ORDER BY [DATE] )
    FROM YourTable
)
SELECT  ID,DATE,LIMIT
       ,VAL = CASE WHEN rt - value > LIMIT THEN 0 
          ELSE CASE WHEN rt > LIMIT  THEN rt - LIMIT ELSE value END END
FROM cte