[解決済み] SQL Serverで実行中の合計を計算する
質問
次のような表があるとします(名称は
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で使用することができます。
特に、aggregating-set-statementのトリックを使ったこの手の手法に興味があります。
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
文は、行を正しい順序で処理します。この問題については、何か明確な答えが得られるかもしれませんね。
でも、もしかしたら他の方法を提案できる人がいるかもしれませんね?
edit: 現在では SqlFiddle を設定し、上記の「update trick」の例とします。
解決するには?
更新情報 SQL Server 2012 を実行している場合は、こちらをご覧ください。 https://stackoverflow.com/a/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は、この問題に対するきれいな解決策を与えてくれません。私の直感では、これはカーソルが最も速い稀なケースの1つだと思いますが、大きな結果についていくつかのベンチマークを行う必要があるでしょう。
更新のトリックは便利ですが、かなり壊れやすいと感じます。テーブル全体を更新する場合、主キーの順番で更新されるようです。つまり、日付を主キーの昇順に設定すると、次のようになります。
probably
は安全です。しかし、あなたは文書化されていないSQL Serverの実装の詳細に依存しています(また、クエリが2つのプロックによって実行されることになった場合、何が起こるのでしょうか、参照してください。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
ベンチマークを求められたので......これがその概要です。
SAFEで最も高速な方法はCursorで、cross-joinの相関サブクエリよりも1桁高速です。
絶対的に早いのは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
-- 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 ServerでSELECTからUPDATEする方法とは?
-
[解決済み] SQL Server で複数行のテキストを 1 つのテキスト文字列に連結する方法
-
[解決済み] SQL Server テーブルにカラムが存在するかどうかを確認する方法は?
-
[解決済み] SQL Server の DateTime データ型から日付だけを返す方法
-
[解決済み] SQL ServerにおけるLEFT JOINとLEFT OUTER JOINの比較
-
[解決済み] SQL ServerでJOINを使用してUPDATE文を実行するにはどうすればよいですか?
-
[解決済み] SQL Server - 挿入された行のIDを取得するための最良の方法は?
-
[解決済み] SQLのIN句をパラメータ化する
-
[解決済み] NOT IN vs NOT EXISTS
-
[解決済み】SQL Serverで既存のテーブルにデフォルト値を持つカラムを追加する
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
windows mysql prompt access denied for user ''@'localhost' to database.
-
[解決済み] ストアドプロシージャ 'dbo.aspnet_CheckSchemaVersion' が見つかりませんでした。
-
[解決済み] Oracle Trigger ORA-04098: トリガーが無効で、再バリデーションに失敗しました。
-
[解決済み] 1つのPostgreSQLクエリで複数のWITHステートメントを使用するには?
-
[解決済み] 各GROUP BYグループの最初の行を選択しますか?
-
[解決済み] 各グループの最後のレコードを取得する - MySQL
-
[解決済み] SQLのIN句をパラメータ化する
-
[解決済み] T-SQLでnot equalには!=と<>のどちらを使うべきですか?
-
[解決済み] Selectステートメントで特定のフィールドの重複を検索する
-
[解決済み] SQL Serverで、ある日付より大きいすべての日付を照会するにはどうすればよいですか?