ProgrammingSQL開発者 / バックエンド開発者

SQLにおける複雑な反復計算を実現するさまざまな方法について説明してください。再帰的共通テーブル式(CTE)を使用すべき場合と、他の手法を使用すべき場合の違いは何ですか?

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

答え。

SQLには、反復的/再帰的計算を実現するためのいくつかの方法があります:

  • 再帰的CTE (WITH RECURSIVE) — 階層(ツリー、グラフ、リンクチェーン)を巡回するようなタスクに機能します。
  • カーソル (CURSOR) — データを行ごとに処理することを可能にしますが、大量のデータに対しては集合ベースの操作に比べてパフォーマンスが劣ります。
  • 通常の集合ベースの式 — ほとんどのケースで好まれる(反復なしで行の集合を操作します)。

再帰的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秒で取得しました。