sql >> データベース >  >> RDS >> Sqlserver

SQLServerで現在の合計を計算する

    更新 、SQL Server 2012を実行している場合は、https://stackoverflow.com/a/10309947

    を参照してください。

    問題は、SQLServerでの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
    

    あなたはベンチマークを求めましたが、これはローダウンです。

    これを行う最も安全な方法はカーソルです。これは、相互結合の相関サブクエリよりも桁違いに高速です。

    絶対に最速の方法は、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
    


    1. pysparkシェルでjdbcを使用してpostgresに接続できません

    2. MySQLで2つの日付の差を計算する方法

    3. MySQLステートメントの実行には1分以上かかります

    4. 日付を含む、Oracleの2つの日付間の日数を取得します