1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SQL Serverストアド設計のアンチパターン

1
Last updated at Posted at 2026-05-05

はじめに

前回は「運用で困らないための設計」を解説しました。

今回はさらに一歩進んで、現場でよくあるアンチパターンと改善方法を紹介します。

この記事では、事故につながりやすいアンチパターンを以下の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())

おわりに

ストアド設計で最も重要なのは「運用で困らないか?」という視点です。

ストアドは一度作ると長く使われます。
だからこそ

  • 速く
  • 安全で
  • 調査しやすい

設計にしておくことが重要です。


1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?