ProgrammingSQL開発者

SQLにおけるカーソルを使用したデータの逐次処理の特徴、利点、および落とし穴について説明してください。カーソルを使用することが望ましい場合と、集合操作(set-based operations)を代わりに使用すべき場合はどのような状況ですか?カーソルの使用例を示してください。

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

回答。

カーソルは、SQLにおいてデータセットを行単位で反復処理し、各レコードに対して特定のアクションを実行することができるため、プログラミングにおける反復処理に類似しています。これにより、SQLの1つのクエリでは不可能な複雑なステップロジックを扱うことができます(例えば、外部状態の変化を伴う段階的な計算など)。

カーソルの例:

DECLARE my_cursor CURSOR FOR SELECT id, balance FROM Accounts WHERE isActive = 1; OPEN my_cursor; DECLARE @id INT, @bal DECIMAL(10,2); FETCH NEXT FROM my_cursor INTO @id, @bal; WHILE @@FETCH_STATUS = 0 BEGIN UPDATE Accounts SET balance = @bal * 1.05 WHERE id = @id; FETCH NEXT FROM my_cursor INTO @id, @bal; END CLOSE my_cursor; DEALLOCATE my_cursor;

利点:

  • 各行が個別に処理または外部条件の検証を必要とする場合に、複雑な手続きの実装が可能。
  • ステップバイステップの制御が必要な古いシステムとの統合や移行に便利。

欠点と落とし穴:

  • カーソルは通常非常に遅い:処理は個別に行われ、セットベースの方法ではないため。
  • 多くのリソースを消費し、テーブルをロックする可能性がある。
  • 大量のデータにはスケールしない。
  • セットベースのSQLに書き換えたり、バッチ処理を使用する方が良い。

トリック質問。

トリガー内でカーソルを使用できますか?それはデータベースのパフォーマンスにどのような影響を与える可能性がありますか?

回答と例: トリガー内でカーソルを使用することは可能ですが、非常に推奨されません。各DML操作は影響を受けた各行についてカーソルを起動する可能性があり、これによりクエリとロックの急増が発生します。

CREATE TRIGGER UpdateBalance ON Accounts AFTER INSERT AS DECLARE c CURSOR FOR SELECT id FROM inserted; -- 悪い! OPEN c; -- ...

歴史

プロジェクト:小売業の注文集計。 不良品が見つかった場合に在庫をカウントする必要があり、次のロットは割引の更新を通じて手動で再計算する必要がありました。ロットを走査するためにカーソルを使用しました。後に、何度も実行するとスレッドが数時間ロックされ、サーバーへの負荷が指数的に増加し、ロック競合によって障害が発生することが判明しました。


歴史

プロジェクト:ERP、データ移行。 インポート処理の手続きにカーソルを使用してエラーハンドリングを追加しました。500万行では、カーソルがset-based UPDATE + CASEを使用した類似のバッチ処理よりも40倍遅くなることを考慮していませんでした。遅い移行のために締切がずれました。


歴史

プロジェクト:金融会社の請求。 資金移動テーブルの更新トリガーにカーソルを追加し、新しい集約残高を計算しました。本番環境では「STOP THE WORLD」に至り、一行の挿入すらサービスの動作を遅らせました。その理由は、多くの行に対してネストされたカーソルが起動されたためです。