如何在不使用游标的情况下计算SQL中的运行总数?

为了简洁起见,我将所有的游标设置和临时表中的SELECT都舍弃了。 基本上,这个代码计算每笔交易的所有交易的运行余额。

WHILE @@fetch_status = 0 BEGIN set @balance = @balance+@amount insert into @tblArTran values ( --from artran table @artranid, @trandate, @type, @checkNumber, @refNumber,@custid, @amount, @taxAmount, @balance, @postedflag, @modifieddate ) FETCH NEXT FROM artranCursor into @artranid, @trandate, @type, @checkNumber, @refNumber, @amount, @taxAmount,@postedFlag,@custid, @modifieddate END 

这个代码从另一个问题的答案启发,

 SELECT @nvcConcatenated = @nvcConcatenated + C.CompanyName + ', ' FROM tblCompany C WHERE C.CompanyID IN (1,2,3) 

我想知道,如果你明白了我的意思,SQL是否有能力以相同的方式对数字进行求和。 也就是说,每行创build一个“运行平衡”,而不使用游标。

可能吗?

您可能需要查看本地variables解决scheme的更新: http : //geekswithblogs.net/Rhames/archive/2008/10/28/calculating-running-totals-in-sql-server-2005—该-optimal.aspx

 DECLARE @SalesTbl TABLE (DayCount smallint, Sales money, RunningTotal money) DECLARE @RunningTotal money SET @RunningTotal = 0 INSERT INTO @SalesTbl SELECT DayCount, Sales, null FROM Sales ORDER BY DayCount UPDATE @SalesTbl SET @RunningTotal = RunningTotal = @RunningTotal + Sales FROM @SalesTbl SELECT * FROM @SalesTbl 

胜过其他所有方法,但对保证行顺序有一些疑问。 似乎工作正常临时表索引虽然..

  • 嵌套子查询9300毫秒
  • 自我join6100毫秒
  • 光标400毫秒
  • 更新到本地variables140毫秒

SQL 可以在不使用游标的情况下创build运行总计,但是这是less数情况下其中一个游标实际上比基于集合的解决scheme(考虑到SQL Server中当前可用的运算符)性能更高的情况之一。 或者,CLRfunction有时可以发光。 Itzik Ben-Gan在SQL Server杂志上做了一个关于运行聚合的优秀系列。 该系列上个月已经结束,但是如果您有在线订阅,则可以访问所有文章。

编辑:这是他最新的系列文章 (SQL CLR)。 考虑到您可以通过购买一个月的在线月票(不到6美元)来访问整个系列,如果您有兴趣从各个angular度来看问题,那么值得您一段时间。 Itzik是一个微软MVP和一个非常明亮的TSQL编码器。

OraclePostgreSQL 8.4你可以使用窗口函数:

 SELECT SUM(value) OVER (ORDER BY id) FROM mytable 

MySQL ,可以使用会话variables来达到同样的目的:

 SELECT @sum := @sum + value FROM ( SELECT @sum := 0 ) vars, mytable ORDER BY id 

SQL Server ,这是一个罕见的游标为首选解决scheme的例子。

计算每个logging的运行总计的示例,但前提是logging的OrderDate在同一date。 一旦订单date不同,那么新的运行总量将在新的一天开始并累计:(假设表结构和数据)

 select O.OrderId, convert(char(10),O.OrderDate,101) as 'Order Date', O.OrderAmt, (select sum(OrderAmt) from Orders where OrderID <= O.OrderID and convert(char(10),OrderDate,101) = convert(char(10),O.OrderDate,101)) 'Running Total' from Orders O order by OrderID 

以下是使用示例订单表查询返回的结果:

 OrderId Order Date OrderAmt Running Total ----------- ---------- ---------- --------------- 1 10/11/2003 10.50 10.50 2 10/11/2003 11.50 22.00 3 10/11/2003 1.25 23.25 4 10/12/2003 100.57 100.57 5 10/12/2003 19.99 120.56 6 10/13/2003 47.14 47.14 7 10/13/2003 10.08 57.22 8 10/13/2003 7.50 64.72 9 10/13/2003 9.50 74.22 

请注意,“Running Total”的起始值为10.50,然后变为22.00,因为所有这些logging都具有相同的OrderDate(10/11/2003),所以OrderID 3最终变为23.25。 但是当显示OrderID 4时,运行总量被重置,运行总量重新开始。 这是因为OrderID 4对于OrderDate有不同的date,所以OrderID 1,2和3.对每个唯一的date计算这个运行总数是通过使用相关的子查询再次完成的,尽pipe需要一个额外的WHERE条件,确定不同logging上的OrderDate需要在同一天。 此WHERE条件是通过使用CONVERT函数将OrderDate截断为MM / DD / YYYY格式来完成的。

在SQL Server 2012中,您可以直接对原始表使用Sum窗口函数:

 SELECT artranid, trandate, type, checkNumber, refNumber, custid, amount, taxAmount, Balance = Sum(amount) OVER (ORDER BY trandate ROWS UNBOUNDED PRECEDING), postedflag, modifieddate FROM dbo.Sales ; 

与所有的解决scheme相比,这将performance得非常好,而且不会像在“古怪的更新”中发现的那样有可能出现错误。

请注意,尽可能使用ROWS版本; RANGE版本可能performance不佳。

您可以在select子句中包含相关的子查询。 (对于非常大的结果集,这将performance不佳)

  Select <other stuff>, (Select Sum(ColumnVal) From Table Where OrderColumn <= T.OrderColumn) As RunningTotal From Table T Order By OrderColumn 

你可以做一个运行计数 ,这里是一个例子,请记住,这实际上并不是那么快,因为它必须扫描每一行的表,如果你的表很大,这可能是相当耗时和昂贵

 create table #Test (id int, Value decimal(16,4)) insert #Test values(1,100) insert #Test values(2,100) insert #Test values(3,100) insert #Test values(4,200) insert #Test values(5,200) insert #Test values(6,200) insert #Test values(7,200) select *,(select sum(Value) from #Test t2 where t2.id <=t1.id) as SumValues from #test t1 id Value SumValues 1 100.0000 100.0000 2 100.0000 200.0000 3 100.0000 300.0000 4 200.0000 500.0000 5 200.0000 700.0000 6 200.0000 900.0000 7 200.0000 1100.0000 

在SQLTeam上还有一篇关于计算运行总数的文章 。 有三种方式做比较,以及一些性能测量:

  • 使用游标
  • 使用子查询(根据SQLMenace的post)
  • 使用CROSS JOIN

游标远远胜过其他的解决scheme,但是如果你不能使用游标,至less还有一个select。

那个SELECT @nvcConcatonated位只返回一个连接的值。 (虽然它是以每行为基础计算中间值的,但只能检索最终值)。

所以,我认为答案是否定的。 如果你想要一个单一的总和值,你当然会使用SUM

我不是说你做不到,我只是说你不能用这个“伎俩”来做。

请注意,在多处理器系统中使用variables来完成此操作可能会失败,因为可以在不同的处理器上计算单独的行,并最终可能会使用相同的起始值。 我的理解是,可以使用查询提示来强制它使用单个线程,但是我没有这方面的信息。

UPDATE @SalesTbl SET @RunningTotal = RunningTotal = @RunningTotal + Sales FROM @SalesTbl

使用其他选项(游标,窗口函数或嵌套查询)通常会成为您最可靠的结果。

selectTransactionDate,amount,amount +(sum x.amount from transactions x where x.TransactionDate <Transactions)TransactionTotal从Transactions Transactions

其中x.TransactionDate <Transactions可以是任何除了当前表示之外的所有先前logging的条件