はじめに
前回は「運用で困らないための設計」を解説しました。
今回はさらに一歩進んで、現場でよくあるアンチパターンと改善方法を紹介します。
この記事では、事故につながりやすいアンチパターンを以下の4つに分けて解説します。
- パフォーマンス問題
- 運用崩壊パターン
- バグを生む書き方
- 設計ミス
パフォーマンス問題
カーソル(Row-by-Row処理)
- 1行ずつ処理するため遅い
- スケールしない
- 可読性が低い
アンチパターン
DECLARE cur CURSOR FOR
SELECT customer_id
FROM m_customer
OPEN cur
FETCH NEXT FROM cur INTO @CustomerId
WHILE @@FETCH_STATUS = 0
BEGIN
UPDATE m_customer
SET status = '1'
WHERE customer_id = @CustomerId
FETCH NEXT FROM cur INTO @CustomerId
END
CLOSE cur
DEALLOCATE cur
ベストプラクティス(集合処理)
UPDATE m_customer
SET status = '1'
ベストプラクティス(条件分岐がある場合)
UPDATE m_customer
SET status = CASE WHEN customer_id % 2 = 0 THEN 'EVEN' ELSE 'ODD' END
インデックス無視
- インデックスが使われない(フルスキャン)
- 大量データで致命的に遅くなる
アンチパターン
WHERE CONVERT(VARCHAR, update_date) = '2026-01-01'
ベストプラクティス
WHERE update_date >= '2026-01-01'
AND update_date < '2026-01-02'
長時間トランザクション
- ロックが長時間保持される
- 他処理が止まる(デッドロックの原因)
アンチパターン
BEGIN TRAN
-- 大量更新
COMMIT
ベストプラクティス
WHILE 1 = 1
BEGIN
UPDATE TOP (1000) m_customer
SET status = '1'
WHERE status = '0'
IF @@ROWCOUNT = 0 BREAK
END
運用崩壊パターン
ログがない
- ジョブ実行では見えない
- 調査が不可能になる
アンチパターン
PRINT '処理開始'
ベストプラクティス
EXEC usp_InsertLog
@ProcessName = 'usp_SampleBatch',
@LogLevel = 1,
@Message = '処理開始'
エラーを握りつぶす
- エラーがあっても成功扱い
- 障害に気づけない
アンチパターン
BEGIN CATCH
PRINT 'エラー'
END CATCH
ベストプラクティス
BEGIN CATCH
IF @@TRANCOUNT > 0
ROLLBACK
SET @ReturnCode = 9
END CATCH
RETURNコードを使っていない
- エラーでも成功扱い
- 監視で検知できない
アンチパターン
EXEC dbo.usp_SampleBatch
ベストプラクティス
DECLARE @return_value INT
EXEC @return_value = dbo.usp_SampleBatch
IF @return_value <> 0
BEGIN
RAISERROR('Batch Error', 16, 1)
END
例外をそのままTHROW
- ジョブ側で制御できない
- リトライ設計ができない
アンチパターン
THROW
ベストプラクティス
BEGIN CATCH
SET @ReturnCode = 9
END CATCH
バグを生む書き方
NULL未考慮
- NULLが対象外になる
- 想定外のデータが残る
アンチパターン
WHERE status <> '1'
ベストプラクティス
WHERE status <> '1'
OR status IS NULL
マジックナンバー
- 意味が分からない
- 修正ミスを誘発
アンチパターン
WHERE status = '1'
ベストプラクティス
WHERE status = @STATUS_ACTIVE
SELECT *
- 影響範囲が不明
- パフォーマンス悪化
- カラム追加で壊れる可能性
アンチパターン
SELECT * FROM m_customer
ベストプラクティス
SELECT customer_id, status
FROM m_customer
設計ミス
再実行を考えていない(冪等性なし)
- 再実行で重複
- データ不整合
アンチパターン
INSERT INTO m_customer VALUES (...)
ベストプラクティス
IF NOT EXISTS (SELECT 1 FROM m_customer WHERE customer_id = @CustomerId)
BEGIN
INSERT INTO m_customer VALUES (...)
END
ハードコード日付
- 将来バグになる
- 運用で破綻する
アンチパターン
WHERE update_date < '2020-01-01'
ベストプラクティス
WHERE update_date < DATEADD(DAY, -365, GETDATE())
おわりに
ストアド設計で最も重要なのは「運用で困らないか?」という視点です。
ストアドは一度作ると長く使われます。
だからこそ
- 速く
- 安全で
- 調査しやすい
設計にしておくことが重要です。