SQLには、反復的/再帰的計算を実現するためのいくつかの方法があります:
WITH RECURSIVE) — 階層(ツリー、グラフ、リンクチェーン)を巡回するようなタスクに機能します。再帰的CTEを使用すべき時:
例:
WITH RECURSIVE tree AS ( SELECT id, parent_id, name, 1 AS level FROM categories WHERE parent_id IS NULL UNION ALL SELECT c.id, c.parent_id, c.name, t.level + 1 FROM categories c JOIN tree t ON c.parent_id = t.id ) SELECT * FROM tree;
再帰的CTEが問題を解決できない場合やデータが大きすぎる場合は、他のメカニズム(たとえば外部処理、一時テーブル、またはSQL外の専門的なアルゴリズム)を適用します。
再帰的CTEが無限実行につながることがありますか?これを防ぐにはどうすればよいですか?
答え:
はい、グラフにサイクルがある場合(たとえば、parent_idが再び子を指す場合)、再帰的CTEは無限ループに陥る可能性があります。ほとんどのDBMSは再帰の最大レベル制限をサポートしています(MS SQLではMAXRECURSIONや同様のオプション)。再帰の深さを常に制限することが良い実践です。
-- MS SQL Server OPTION (MAXRECURSION 100)
ストーリー
金融プロジェクトでは、再帰的CTEを使用して関連するアカウントのチェーンに基づいて最終バランスを計算していました。 ユーザーの1人が構造の中にサイクルを作成しました(親が自分の子供になった)。クエリは無限に実行され、サーバーに負荷をかけました。サイクルの検出とMAXRECURSIONを追加し、問題が解決されました。
ストーリー
タスク管理システムでは、依存タスクのステータスをカーソル処理で計算していました。 再帰的CTEに移行した後、parent_idに最適なインデックスを考慮するのを忘れたため、パフォーマンスが5倍低下しました。各ステップでコストのかかる結合操作が発生していたためです。
ストーリー
カーソルを使用したカテゴリーのツリーを巡回するクエリは正しく動作していましたが、100,000行で7分かかりました。 再帰的CTEに書き直したところ、「遅い」と見なされていたにもかかわらず、最適化のおかげで同じ結果を5秒で取得しました。