【问题标题】:SUM() OVER With Condition Based on Current Sum ValueSUM() OVER 条件基于当前总和值
【发布时间】:2014-08-23 14:36:53
【问题描述】:

我有一个表格(TableA),有点像这样:

Id  Type    TimeBlock   Value
1   1       1           100
2   1       1           150
3   2       1           100
4   1       1           1000
5   1       1           100
6   1       2           50
6   1       2           50

我希望能够采用 TimeBlock 对它进行分区的 Value 列的 SUM,但是我只想根据某些条件将行划分为 SUM。如果当前总和值,则条件是仅SUM 行 大于或等于 Value 列

我假设查询将以以下内容开头:

DECLARE @Amount int = 500;
SELECT *, @Amount - SUM (Value) OVER (PARTITION BY TimeBlock ORDER BY Id) AS Sum
FROM TableA

我希望结果如下所示:

Id  Type    TimeBlock   Value   Sum
1   1       1           100     400     (500 - 100)
2   1       1           150     250     (400 - 150)
3   2       1           1000    NULL    (This does not count since its of type = 2 and 250 < 1000)
4   1       1           1000    0       (250 - 1000 = -750 there its filled the @Amount so we'll leave it at 0)
5   1       1           100     NULL    (Since @Amount has been filled anything that comes after in this time block is NULL)
6   1       2           50      450     (500 - 50)
6   1       2           50      400     (450 - 50)

如您所见,我需要能够获取 SUM 函数的当前值,以便能够将其与我不太确定该怎么做的 Value 列进行比较。我希望能够做类似的事情

DECLARE @Amount int = 500;
SELECT *, @Amount - SUM (CASE WHEN Type = 2 AND SUM(Value) OVER (PARTITION BY TimeBlock ORDER BY Id) < Value THEN 0 ELSE Value END) OVER (PARTITION BY TimeBlock ORDER BY Id) AS Sum
FROM TableA

编辑 Type = 1 - 无论如何都要对 Value 列求和 Type = 2 - 如果当前总和值大于或等于 Value,则仅对行中的 Value 列求和

【问题讨论】:

  • 你能更好地阐明规则吗? type 和什么有什么关系?
  • @GordonLinoff - 请查看我的编辑
  • 我认为 type = 2 的问题提出了一个使用累积和无法轻易解决的问题。光标是一种选择;另一个是递归 CTE(它也将迭代)。当您有多个 type = 2 时会出现问题 - 是否包含第二个不是一个简单的公式,这取决于是否包含第一个。

标签: sql sql-server-2012


【解决方案1】:

虽然它基于有争议的Running Sum Quirky Update(由 Jeff Moden here 更详细地描述),但以下代码将起作用:

-- prepare temporary table with required clustered index to control processing order
create table #t (
     Id         int not null primary key nonclustered
    ,Type       int not null
    ,TimeBlock  int not null
    ,Value      int not null

    ,RunningSum int null

    ,unique clustered (TimeBlock,Id)
);

-- load temporary table from data source, 
-- hard-coded data used for this example.
insert #t(Id, Type,TimeBlock,Value)
values
 (1, 1,  1,    100)
,(2, 1,  1,    150)
,(3, 2,  1,    100)
,(4, 1,  1,   1000)
,(5, 1,  1,    100)
,(6, 1,  2,     50)
,(7, 1,  2,     50);

go

declare @sum        int = 0
       ,@TimeBlock  int = -1;

update #t
    set @sum = case when TimeBlock <> @TimeBlock then 500 - Value
                    when Value > @sum            then @sum

                    else @sum - Value
                end
       ,@TimeBlock = TimeBlock
       ,RunningSum = @sum
from #t with (TABLOCKX)
where Type = 1
option (MAXDOP 1)
;

-- display results
select * from #t

drop table #t;
go

输出:

Id          Type        TimeBlock   Value       RunningSum
----------- ----------- ----------- ----------- -----------
1           1           1           100         400
2           1           1           150         250
3           2           1           100         NULL
4           1           1           1000        250
5           1           1           100         150
6           1           2           50          450
7           1           2           50          400

请特别注意,这要求聚集索引与排序相同,并确保不会被其他管理员修改,将数据复制到具有所需聚集索引的临时表中。此外,选项 (MAXDOP 1) 设置为确保优化器不会尝试并行化查询,并设置 EXCLUSIVE 锁以便(如果不需要临时表)不进行修改在处理过程中可能会发生表的删除。

更新Quirky Update 是有争议的,因为尽管它自 Sybase SQL Server 的早期就开始工作,但它依赖于 SQL Server 的未记录功能不保证在未来的版本中受支持。

【讨论】:

  • 我有什么理由应该使用这种方法而不是使用光标?
  • @Ryan:速度,纯粹而简单;查看 Jeff Moden(RBAR 一词的创始人)在我链接到的文章中描述的性能测试。如果您的数据量适中,比如少于几千行,那么游标可能同样合适。
  • 这对我来说效果很好,但是我不得不用 @sum 变量的 CASE 语句更改一些逻辑
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-01
  • 2021-03-01
  • 1970-01-01
  • 2017-04-04
  • 1970-01-01
  • 2021-11-05
相关资源
最近更新 更多