累積合計や移動合計の計算は、従来SQLではウィンドウ関数(例:SUM() OVER(ORDER BY ...))を用いて行われましたが、古いまたは簡易版のDBMSではサブクエリとグルーピングしか利用できませんでした。歴史的にDBアーキテクトは、ウィンドウ関数をサポートするSQL:2003の基準が登場する前に回避策を模索していました。
問題 — ウィンドウ関数がない場合、各行についてすべての前の値の合計を明示的に計算する必要があり、十分に大きなデータセットではO(N^2)のネストされたクエリを引き起こします。
解決策:
通常、相関サブクエリや値を更新するための一時テーブルを使用します。
コード例:
-- 相関サブクエリによる累積合計 SELECT t1.id, t1.amount, ( SELECT SUM(t2.amount) FROM transactions t2 WHERE t2.id <= t1.id ) AS running_total FROM transactions t1 ORDER BY t1.id; -- 手動で値を更新する一時テーブルを使用 CREATE TEMPORARY TABLE temp_running (id INT, amount INT, running_total INT); -- 外部コード(例:pl/pgsql)を使用して行を処理し、合計を順次追加
重要なポイント:
相関サブクエリのORDER BYは保証されたソートを提供しますか?
いいえ — サブクエリは結果に必ず影響を与えるわけではありません。最終的な結果のソートは常に主クエリで外部指定され、結果はWHEREによるフィルタリングにのみ依存します。
このアプローチで累積合計の計算を並列処理できますか?
いいえ — 順序は非常に重要であり、特に前の行に基づく計算では、通常のSQLでの単純な並列化は不可能です。
なぜ相関サブクエリは大量の行でそんなに遅いのですか?
各行ごとに、以前の行セットに対して合計を再計算するからです。これによりO(N^2)の操作が発生します。10万行のサンプルでは、これが数分または数時間かかる可能性があります。
アナリストは相関サブクエリを通じて日にちごとの累積収益を計算しましたが、テーブルには定期的に削除されたid(ギャップ)が現れていました。最終的な合計は激しい変動があり、日付に依存せずidの順序に依存していました。
長所:
短所:
エンジニアは累積合計の処理をETLスクリプト(Python/pandas)に移行し、最終的な値を別のテーブルにロードし、新しいもののみを同期しました。合計は常に日付に一致し、コードは数百万のレコードで素早く機能します。
長所:
短所: