想象一下下表(称为TestTable):

id     somedate    somevalue
--     --------    ---------
45     01/Jan/09   3
23     08/Jan/09   5
12     02/Feb/09   0
77     14/Feb/09   7
39     20/Feb/09   34
33     02/Mar/09   6

我想要一个按日期顺序返回运行总数的查询,如:

id     somedate    somevalue  runningtotal
--     --------    ---------  ------------
45     01/Jan/09   3          3
23     08/Jan/09   5          8
12     02/Feb/09   0          8
77     14/Feb/09   7          15  
39     20/Feb/09   34         49
33     02/Mar/09   6          55

我知道SQL Server 2000/2005/2008中有various ways of doing this个.

我对这种使用聚合集语句技巧的方法特别感兴趣:

INSERT INTO @AnotherTbl(id, somedate, somevalue, runningtotal) 
   SELECT id, somedate, somevalue, null
   FROM TestTable
   ORDER BY somedate

DECLARE @RunningTotal int
SET @RunningTotal = 0

UPDATE @AnotherTbl
SET @RunningTotal = runningtotal = @RunningTotal + somevalue
FROM @AnotherTbl

... 因为我不能保证这句话的顺序是正确的.也许我们可以得到一些关于这个问题的明确答案.

但也许人们还能提出其他建议?

编辑:现在有一个SqlFiddle与设置和'更新技巧'以上的例子

推荐答案

Update,如果您运行的是SQL Server 2012,请参阅:https://stackoverflow.com/a/10309947

问题是Over子句的SQL Server实现是somewhat limited.

Oracle(和ANSI-SQL)允许您执行以下操作:

 SELECT somedate, somevalue,
  SUM(somevalue) OVER(ORDER BY somedate 
     ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) 
          AS RunningTotal
  FROM Table

SQL Server并没有为这个问题提供干净的解决方案.我的直觉告诉我,这是光标最快的罕见 case 之一,尽管我必须对重大结果进行一些基准测试.

更新技巧很方便,但我觉得它相当脆弱.如果您正在更新一个完整的表,那么它将按照主键的顺序进行.所以,如果你把日期设为主键,你就安全了.但您依赖的是一个未记录的SQL Server实现细节(如果查询最终由两个过程执行,我想知道会发生什么,请参阅:MAXDOP):

完整工作样本:

drop table #t 
create table #t ( ord int primary key, total int, running_total int)

insert #t(ord,total)  values (2,20)
-- notice the malicious re-ordering 
insert #t(ord,total) values (1,10)
insert #t(ord,total)  values (3,10)
insert #t(ord,total)  values (4,1)

declare @total int 
set @total = 0
update #t set running_total = @total, @total = @total + total 

select * from #t
order by ord 

ord         total       running_total
----------- ----------- -------------
1           10          10
2           20          30
3           10          40
4           1           41

你要求提供一个基准,这是事实真相.

最快的安全方法是使用光标,它比交叉连接的相关子查询快一个数量级.

最快的方法就是更新技巧.我唯一担心的是,我不确定在所有情况下,更新都会以线性方式进行.查询中没有明确说明这一点.

总之,对于生产代码,我将使用光标.

测试数据:

create table #t ( ord int primary key, total int, running_total int)

set nocount on 
declare @i int
set @i = 0 
begin tran
while @i < 10000
begin
   insert #t (ord, total) values (@i,  rand() * 100) 
    set @i = @i +1
end
commit

测试1:

SELECT ord,total, 
    (SELECT SUM(total) 
        FROM #t b 
        WHERE b.ord <= a.ord) AS b 
FROM #t a

-- CPU 11731, Reads 154934, Duration 11135 

测试2:

SELECT a.ord, a.total, SUM(b.total) AS RunningTotal 
FROM #t a CROSS JOIN #t b 
WHERE (b.ord <= a.ord) 
GROUP BY a.ord,a.total 
ORDER BY a.ord

-- CPU 16053, Reads 154935, Duration 4647

测试3:

DECLARE @TotalTable table(ord int primary key, total int, running_total int)

DECLARE forward_cursor CURSOR FAST_FORWARD 
FOR 
SELECT ord, total
FROM #t 
ORDER BY ord


OPEN forward_cursor 

DECLARE @running_total int, 
    @ord int, 
    @total int
SET @running_total = 0

FETCH NEXT FROM forward_cursor INTO @ord, @total 
WHILE (@@FETCH_STATUS = 0)
BEGIN
     SET @running_total = @running_total + @total
     INSERT @TotalTable VALUES(@ord, @total, @running_total)
     FETCH NEXT FROM forward_cursor INTO @ord, @total 
END

CLOSE forward_cursor
DEALLOCATE forward_cursor

SELECT * FROM @TotalTable

-- CPU 359, Reads 30392, Duration 496

测试4:

declare @total int 
set @total = 0
update #t set running_total = @total, @total = @total + total 

select * from #t

-- CPU 0, Reads 58, Duration 139

Sql相关问答推荐

如何以"% m—% d"格式对生日列表进行排序,以查找与今天最近的日期?

有没有一种正确的方法来利用SQL UNION来从三个潜在查询中 Select 最大值?

替换条件中的单元格值

使用占位符向SQL INSERT查询添加 case

如何更新SQLite数据库中的表?

存储过程太慢

向表中添加新列取决于表的日期列(unpivot)

查询以从时间范围列表中查找唯一时间段

如何修复初学者 SQL INNER JOIN 查询错误

如何在Hive SQL中分别按多列进行分组?

用户定义的标量值函数是否仍然会阻止并行性?

识别SQL Server中的重复数字

如何在sparksql查询中使用日期值?

IN子句使用的表值用户定义函数参数

多行状态下的分组查询判断状态

批量更改WooCommerce中所有产品的税收状态

postgres按组消除分区中的NULLS

雅典娜弄错了操作顺序

为什么 Oracle 在一个查询中对同一张表同时执行 TABLE SCAN 和 INDEX UNIQUE SCAN?

如何在 Oracle 中获取此变量的值?