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

パーティション化すると、現在の合計クエリが生成されます

    データを保存する必要がない場合(行が変更、追加、または削除されるたびに現在の合計を更新する必要があるため、保存しないでください)、および風変わりな更新を信頼しない場合(動作が保証されておらず、修正プログラム、サービスパック、アップグレード、または基になるインデックスや統計の変更によって動作が変わる可能性があるため、このタイプのクエリを実行時に試すことができます。これは、MVPの仲間であるHugo Kornelisが「セットベースの反復」を作成した方法です(彼はの章の1つに同様の記事を投稿しました) SQLServerMVPディープダイブ )。通常、現在の合計には、セット全体のカーソル、セット全体の風変わりな更新、または行数が増えるにつれてますます高価になる単一の非線形自己結合が必要になるため、ここでの秘訣は、有限要素をループすることです。セット内の要素(この場合、各ユーザーの月に関する各行の「ランク」-そして、そのランクのすべてのユーザー/月の組み合わせに対して各ランクを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%サポートされており、サポートされていない風変わりな更新と比較してパフォーマンスを比較する価値があります。勝てなくても近づいても、とにかくIMHOでの使用を検討してください。

    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



    1. MySQLテーブルに配列を格納するためのBLOBとVARCHAR

    2. エラー1452(23000):子行を追加または更新できません:外部キー制約が失敗します

    3. SQLITEは読み取り専用データベースをバージョン1から2にアップグレードできません

    4. 単一のSQLステートメントを使用して1つのテーブルから別のテーブルにレコードを移動することは可能ですか?