177

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 でこれを行うにはさまざまな方法があることを私は知っています。

私が特に興味を持っているのは、集合設定文のトリックを使用するこの種の方法です。

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

UPDATE...これは非常に効率的ですが、ステートメントが行を正しい順序で処理することを必ずしも保証できないため、これには問題があると聞きました。その問題について決定的な答えが得られるかもしれません。

しかし、人々が提案できる他の方法があるのではないでしょうか?

編集:セットアップと上記の「更新トリック」の例を含むSqlFiddleを使用

4

15 に答える 15

130

SQL Server 2012 では、 SUM()OVER()句とともに使用できます。

select id,
       somedate,
       somevalue,
       sum(somevalue) over(order by somedate rows unbounded preceding) as runningtotal
from TestTable

SQL フィドル

于 2012-04-25T05:43:26.143 に答える
40

Sam Saffron は、この問題に対して素晴らしい作業を行いましたが、この問題に対する再帰的な共通テーブル式のコードをまだ提供していません。そして、デナリではなく SQL Server 2008 R2 を使用している私たちにとって、これは実行中の合計を取得する最速の方法であり、100000 行の私の職場のコンピューターのカーソルよりも約 10 倍速く、インライン クエリでもあります。したがって、ここにあります(テーブルに列があり、ギャップのない連続番号で
あると仮定しています。高速処理のために、この番号にも一意の制約が必要です):ord

;with 
CTE_RunningTotal
as
(
    select T.ord, T.total, T.total as running_total
    from #t as T
    where T.ord = 0
    union all
    select T.ord, T.total, T.total + C.running_total as running_total
    from CTE_RunningTotal as C
        inner join #t as T on T.ord = C.ord + 1
)
select C.ord, C.total, C.running_total
from CTE_RunningTotal as C
option (maxrecursion 0)

-- CPU 140, Reads 110014, Duration 132

sql fiddle demo

更新変数または風変わりな updateを使用し たこの更新についても興味がありました。通常は問題なく動作しますが、毎回動作することを確認するにはどうすればよいでしょうか? さて、ここにちょっとしたトリックがあります(ここにあります- http://www.sqlservercentral.com/Forums/Topic802558-203-21.aspx#bm981258)-現在と以前を確認し、それらが何と異なる場合に備えて割り当てordを使用するだけです1/0あなたが期待:

declare @total int, @ord int

select @total = 0, @ord = -1

update #t set
    @total = @total + total,
    @ord = case when ord <> @ord + 1 then 1/0 else ord end,
    ------------------------
    running_total = @total

select * from #t

-- CPU 0, Reads 58, Duration 139

テーブルに適切なクラスター化されたインデックス/主キーがある場合 (この場合は index by ord_id)、更新は常に線形に進行します (0 による除算は発生しません)。とはいえ、本番コードで使用するかどうかはあなた次第です:)

更新 2この回答をリンクしています。風変わりな更新 - nvarchar concatenation / index / nvarchar(max) inexplicable behaviorの信頼性の欠如に関する有用な情報が含まれているためです。

于 2012-12-06T13:23:07.293 に答える
28

SQL 2005 以降の APPLY 演算子は、これに対して機能します。

select
    t.id ,
    t.somedate ,
    t.somevalue ,
    rt.runningTotal
from TestTable t
 cross apply (select sum(somevalue) as runningTotal
                from TestTable
                where somedate <= t.somedate
            ) as rt
order by t.somedate
于 2009-06-05T18:04:08.633 に答える
7

相関サブクエリを使用します。非常に簡単です。

SELECT 
somedate, 
(SELECT SUM(somevalue) FROM TestTable t2 WHERE t2.somedate<=t1.somedate) AS running_total
FROM TestTable t1
GROUP BY somedate
ORDER BY somedate

コードは正確ではないかもしれませんが、アイデアは正しいと確信しています。

GROUP BY は、日付が複数回表示される場合に使用します。結果セットで 1 回だけ表示する必要があります。

日付が繰り返されても構わない場合、または元の値と ID を確認したい場合は、次のようにします。

SELECT 
id,
somedate, 
somevalue,
(SELECT SUM(somevalue) FROM TestTable t2 WHERE t2.somedate<=t1.somedate) AS running_total
FROM TestTable t1
ORDER BY somedate
于 2014-09-12T20:49:11.560 に答える
5

You can also denormalize - store running totals in the same table:

http://sqlblog.com/blogs/alexander_kuznetsov/archive/2009/01/23/denormalizing-to-enforce-business-rules-running-totals.aspx

Selects work much faster than any other solutions, but modifications may be slower

于 2009-06-05T18:14:17.340 に答える
2

以下の単純な INNER JOIN 操作を使用して、現在の合計を達成できると思います。

SELECT
     ROW_NUMBER() OVER (ORDER BY SomeDate) AS OrderID
    ,rt.*
INTO
    #tmp
FROM
    (
        SELECT 45 AS ID, CAST('01-01-2009' AS DATETIME) AS SomeDate, 3 AS SomeValue
        UNION ALL
        SELECT 23, CAST('01-08-2009' AS DATETIME), 5
        UNION ALL
        SELECT 12, CAST('02-02-2009' AS DATETIME), 0
        UNION ALL
        SELECT 77, CAST('02-14-2009' AS DATETIME), 7
        UNION ALL
        SELECT 39, CAST('02-20-2009' AS DATETIME), 34
        UNION ALL
        SELECT 33, CAST('03-02-2009' AS DATETIME), 6
    ) rt

SELECT
     t1.ID
    ,t1.SomeDate
    ,t1.SomeValue
    ,SUM(t2.SomeValue) AS RunningTotal
FROM
    #tmp t1
    JOIN #tmp t2
        ON t2.OrderID <= t1.OrderID
GROUP BY
     t1.OrderID
    ,t1.ID
    ,t1.SomeDate
    ,t1.SomeValue
ORDER BY
    t1.OrderID

DROP TABLE #tmp
于 2011-02-03T18:38:35.567 に答える
0
BEGIN TRAN
CREATE TABLE #Table (_Id INT IDENTITY(1,1) ,id INT ,    somedate VARCHAR(100) , somevalue INT)


INSERT INTO #Table ( id  ,    somedate  , somevalue  )
SELECT 45 , '01/Jan/09', 3 UNION ALL
SELECT 23 , '08/Jan/09', 5 UNION ALL
SELECT 12 , '02/Feb/09', 0 UNION ALL
SELECT 77 , '14/Feb/09', 7 UNION ALL
SELECT 39 , '20/Feb/09', 34 UNION ALL
SELECT 33 , '02/Mar/09', 6 

;WITH CTE ( _Id, id  ,  _somedate  , _somevalue ,_totvalue ) AS
(

 SELECT _Id , id  ,    somedate  , somevalue ,somevalue
 FROM #Table WHERE _id = 1
 UNION ALL
 SELECT #Table._Id , #Table.id  , somedate  , somevalue , somevalue + _totvalue
 FROM #Table,CTE 
 WHERE #Table._id > 1 AND CTE._Id = ( #Table._id-1 )
)

SELECT * FROM CTE

ROLLBACK TRAN
于 2016-11-02T10:07:36.420 に答える