ほとんどのSQL実装(例えば、T-SQL、PL/pgSQL、PL/SQL)では、制御構造が利用可能です:条件(IF/CASE)、ループ(WHILE/FOR/LOOP)、エラーハンドリング構造。これらは、サーバー上でビジネスロジックを自動化するために便利です — 例えば、複雑なレポートの作成、ビジネスルールの検証、ETLプロセスのオーケストレーション、イベントへの反応の自動化に役立ちます。
しかし、SQL手続き内でループや複雑な条件を過剰に使用すると、可読性とパフォーマンスの低下(SQLが「命令的地獄」に変わる — procedural hellの効果)を招く可能性があります。
これらの構造は、ネットワーク(セットベース)処理の手段で実現できない操作にのみ使用することをお勧めします:例えば、動的なロジックを持つ1つのレコードの処理など。
CREATE PROCEDURE ProcessOrders AS BEGIN DECLARE @OrderID INT, @Total FLOAT DECLARE OrderCursor CURSOR FOR SELECT OrderID FROM Orders WHERE Status = 'NEW' OPEN OrderCursor FETCH NEXT FROM OrderCursor INTO @OrderID WHILE @@FETCH_STATUS = 0 BEGIN SELECT @Total = SUM(Price) FROM OrderItems WHERE OrderID = @OrderID IF @Total > 10000 UPDATE Orders SET Status='MANUAL_CHECK' WHERE OrderID=@OrderID ELSE UPDATE Orders SET Status='APPROVED' WHERE OrderID=@OrderID FETCH NEXT FROM OrderCursor INTO @OrderID END CLOSE OrderCursor DEALLOCATE OrderCursor END
CASEはSELECTの中で入れ子のIF条件を置き換えられるのか、また、それらは相互に置き換え可能か?
回答: いいえ。CASEはSELECT/UPDATE/INSERTの式でのみ適用でき、計算フィールドに使用します。IFは手続きを持つブロックの中でしか使用できず、「クリーンな」SELECTでは許可されていません。
例:
SELECT CASE WHEN score > 80 THEN 'High' ELSE 'Low' END AS Level FROM students -- IFはここでは不可能
ストーリー
チームは、コードから全てのビジネスプロセスをSQL手続きに再記述しようとし、多数の入れ子IF/ELSEおよびループを使用しました。その結果、混乱したデバッグ不可能なコードになり、サポートがほぼ不可能に。ロジックをアプリケーションに戻し、重要な手続きのみを残すことになりました。
ストーリー
PL/pgSQLで、EXITのないLOOPの誤った使用が原因でストアドプロシージャに「無限」ループが発生し、DB接続がブロックされました。ループの繰り返し回数の制限と適切な出口条件を導入することで解決されました。
ストーリー
受注処理手続き内の入れ子IFのロジックエラー:ELSE構造が欠けていました。一部の注文が不正なステータス(NULL)のままとなり、後の検証が不足していました。明示的なデフォルトの振る舞いを持つポイントのELSEを追加しました。