データを保存する必要がない場合 (行が変更、追加、または削除されるたびに実行中の合計を更新する必要があるため、そうすべきではありません)、および風変わりな更新を信頼しない場合 (これは、動作が保証されておらず、ホットフィックス、サービス パック、アップグレード、または基になるインデックスや統計の変更によって動作が変わる可能性があるため、実行時にこのタイプのクエリを試すことができます。これは、MVP の仲間である Hugo Kornelis が「セットベースの反復」を作成したメソッドです (彼は、SQL Server MVP Deep Divesの章の 1 つに同様のことを投稿しました)。)。通常、合計を実行するには、セット全体に対するカーソル、セット全体に対する風変わりな更新、または行数が増えるにつれてますますコストがかかる単一の非線形自己結合が必要になるため、ここでのトリックは、いくつかの有限をループすることです。セット内の要素 (この場合、各ユーザーの月単位の各行の「ランク」。そのランクのすべてのユーザー/月の組み合わせに対して各ランクのみを 1 回処理するため、200,000 行をループする代わりに、最大 24 回ループします)。
DECLARE @t TABLE
(
[user_id] INT,
[month] TINYINT,
total DECIMAL(10,1),
RunningTotal DECIMAL(10,1),
Rnk INT
);
INSERT @t SELECT [user_id], [month], total, total,
RANK() OVER (PARTITION BY [user_id] ORDER BY [month])
FROM dbo.my_table;
DECLARE @rnk INT = 1, @rc INT = 1;
WHILE @rc > 0
BEGIN
SET @rnk += 1;
UPDATE c SET RunningTotal = p.RunningTotal + c.total
FROM @t AS c INNER JOIN @t AS p
ON c.[user_id] = p.[user_id]
AND p.rnk = @rnk - 1
AND c.rnk = @rnk;
SET @rc = @@ROWCOUNT;
END
SELECT [user_id], [month], total, RunningTotal
FROM @t
ORDER BY [user_id], rnk;
結果:
user_id month total RunningTotal
------- ----- ----- ------------
1 1 2.0 2.0
1 2 1.0 3.0
1 3 3.5 6.5 -- I think your calculation is off
2 1 0.5 0.5
2 2 1.5 2.0
2 3 2.0 4.0
もちろん、このテーブル変数からベース テーブルを更新することはできますが、それらの保存された値は、次にテーブルが DML ステートメントによって処理されるまでしか有効ではないため、わざわざする必要はありません。
UPDATE mt
SET cumulative_total = t.RunningTotal
FROM dbo.my_table AS mt
INNER JOIN @t AS t
ON mt.[user_id] = t.[user_id]
AND mt.[month] = t.[month];
いかなる種類の暗黙的な順序付けにも依存していないため、これは 100% サポートされており、サポートされていない風変わりな更新と比較してパフォーマンスを比較する価値があります。たとえそれが勝てなくても、私見でそれを使用することを検討する必要があります。
SQL Server 2012 ソリューションについては、Matt が言及していますRANGE
が、この方法ではディスク上のスプールを使用するためROWS
、単に実行するのではなく でテストする必要がありますRANGE
。あなたの場合の簡単な例を次に示します。
SELECT
[user_id],
[month],
total,
RunningTotal = SUM(total) OVER
(
PARTITION BY [user_id]
ORDER BY [month] ROWS UNBOUNDED PRECEDING
)
FROM dbo.my_table
ORDER BY [user_id], [month];
RANGE UNBOUNDED PRECEDING
これを使用するか使用しないかを比較します (ディスク上のスプールROWS\RANGE
も使用します)。RANGE
上記は、プランが少し複雑に見えますが (追加のシーケンス プロジェクト オペレーター)、全体の期間が短くなり、I/O が大幅に少なくなります。
私は最近、特定の実行中の合計シナリオで観察したいくつかのパフォーマンスの違いを概説するブログ投稿を公開しました。
http://www.sqlperformance.com/2012/07/t-sql-queries/running-totals