Programmingバックエンド開発者

SQLでウィンドウ関数なしで累積(逐次的)合計を計算する方法は、数千または数百万行のパフォーマンスを考慮してどのように実装しますか?

Hintsage AIアシスタントで面接を突破

回答

累積合計や移動合計の計算は、従来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)を使用して行を処理し、合計を順次追加

重要なポイント:

  • この方法は、ユニークソート基準(id、生成日)がある場合にのみ機能します。
  • 相関サブクエリはスケーラビリティが悪い — 実行時間が指数関数的に増加します。
  • 大規模データには、SQL外での集約や手続きを用いるETLを使用する方が理にかなっています。

トリック問題。

相関サブクエリのORDER BYは保証されたソートを提供しますか?

いいえ — サブクエリは結果に必ず影響を与えるわけではありません。最終的な結果のソートは常に主クエリで外部指定され、結果はWHEREによるフィルタリングにのみ依存します。

このアプローチで累積合計の計算を並列処理できますか?

いいえ — 順序は非常に重要であり、特に前の行に基づく計算では、通常のSQLでの単純な並列化は不可能です。

なぜ相関サブクエリは大量の行でそんなに遅いのですか?

各行ごとに、以前の行セットに対して合計を再計算するからです。これによりO(N^2)の操作が発生します。10万行のサンプルでは、これが数分または数時間かかる可能性があります。

一般的な間違いやアンチパターン

  • 実際の日付ではなくidで不適切にフィルタリングする — idの欠落で合計が「跳ねる」
  • データを並べ替えずに合計を計算しようとする
  • 巨大なテーブルにこの方法を使用すること — ETLやパーティション処理が必要

実生活の例

ネガティブケース

アナリストは相関サブクエリを通じて日にちごとの累積収益を計算しましたが、テーブルには定期的に削除されたid(ギャップ)が現れていました。最終的な合計は激しい変動があり、日付に依存せずidの順序に依存していました。

長所:

  • 小規模なデータセットには機能する、ウィンドウ関数は不要

短所:

  • データが不正確で、期待通りに計算されない
  • 複雑なサポート

ポジティブケース

エンジニアは累積合計の処理をETLスクリプト(Python/pandas)に移行し、最終的な値を別のテーブルにロードし、新しいもののみを同期しました。合計は常に日付に一致し、コードは数百万のレコードで素早く機能します。

長所:

  • 信頼性、ダウンタイムなしでの再計算の可能性
  • 大規模なデータのサポート

短所:

  • 複雑なランドスケープ — 外部処理ツールが必要