更新,如果你正在运行SQL Server 2012,请参考:https://dev59.com/-XRA5IYBdhLWcg3wsgHq#10309947
问题在于SQL Server实现的Over子句相对有限。
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没有为这个问题提供一个清洁的解决方案。我的直觉告诉我,这是那些罕见情况之一,其中光标是最快的,不过我将不得不对大型结果进行一些基准测试。
更新技巧很方便,但我感觉它相当脆弱。似乎如果您要更新整张表,则它会按照主键的顺序进行。因此,如果您将日期设置为升序的主键,则可能
是安全的。但您正在依赖未经记录的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)
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
您要求进行基准测试,以下是简要说明。
最快且安全的方法是使用游标,它比交叉联接的相关子查询快一个数量级。
绝对最快的方法是使用 UPDATE 技巧。我唯一担心的是,在所有情况下都不能确定更新是否会以线性方式进行。在查询中没有明确表明这一点。
总之,对于生产代码,我建议使用游标。
测试数据:
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
测试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
测试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
测试 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