世界を動かす技術を、日本語で。

SQLアンチパターン

概要

  • SQLアンチパターン は保守性やパフォーマンス低下の原因
  • CASE WHENの乱用インデックスを活かせない記述 がよく見られる
  • **SELECT ** *や SELECT DISTINCT の誤用も注意点
  • ビューの多重化ネストされたサブクエリ の過剰利用はパフォーマンス悪化の要因
  • 設計段階での工夫チームでのベストプラクティス共有 が重要

よくある高インパクトSQLアンチパターン

  • 大規模CASE WHEN文 のビュー内限定実装

    • アプリケーションのステータスコードを 巨大なCASE WHEN で英語化するケース
    • 個別ビュー 内でのみ変換ロジックを記述しがち
    • 他開発者が コピペロジック未使用 となり、 データ整合性の崩壊 を招く
    • ディメンションテーブル共通ビュー で一元管理推奨
  • インデックスを活かせないWHERE句

    • 例: WHERE UPPER(name) = 'ABC'
    • インデックスが効かず 全件スキャン 発生
    • 解決策:
      • 比較値を 小文字正規化 して保存
      • UPPER(name) のインデックス付与
  • **SELECT ** *の安易な使用

    • スキーマ変更時に ビューが壊れる リスク
    • 不要なカラム まで取得しパフォーマンス低下
  • SELECT DISTINCT による重複排除

    • 不完全なJOIN誤ったテーブル関係 が原因の重複を 一時的に隠す
    • 根本原因を放置し メトリクス不整合集計ミス を招く
    • 適切な JOIN条件 の見直しが必須
  • ビュー多重化(View on View)

    • 初期は モジュール化・整理 のつもりで実装
    • 時間経過とともに 依存関係が複雑化
    • パフォーマンス劣化デバッグ困難化
    • 一定期間ごとに ロジックのフラット化マテリアライズ を推奨
  • ネストされたサブクエリの過剰利用

    • サブクエリの多重ネストで 可読性・保守性低下
    • 例: 3~4層 のサブクエリ+ 5000行超のSQL
    • CTE(WITH句) 活用で読みやすさ向上

SQLアンチパターンへの対処とチーム開発

  • SQLはシンプルに見えて、システム規模拡大で 複雑化
  • アンチパターンは 納期優先や小さな妥協 が積み重なって発生
  • 生産コード同様、SQLも 共有・バージョン管理・レビュー・最適化 が重要
  • 設計段階の工夫後工程の手戻り防止 につながる
  • Bill Karwin著 "SQL Anti-patterns" の参照推奨

Hackerたちの意見

インデックスで関数を使う部分は、もっと明確で深い説明が必要だと思う。インデックスで関数を使うと、データのフルスキャンになっちゃうんだよね。クエリランナーがすべての行と列に関数を実行しなきゃいけないから、インデックスの利点が消えちゃう。残念ながら、これは痛い目を見て学んだよ!

このトピックに関する有名なドキュメント - https://use-the-index-luke.com/sql/where-clause/obfuscation

「残念ながら、これは痛い目を見て学んだよ!」…これ、SQL開発者のモットーみたいだね。一方で、結構安定した言語(方言のファミリー?)みたいだから、落とし穴を見つけるのは長い目で見れば役立つよね。

提示された解決策(インデックス付きのUPPER(name)カラムを作る)は、少なくともMS SQL Serverでは最良の方法じゃないと思う。他のデータベースでも同じようにサポートされてるかは分からないけど、より良い解決策はケースインセンシティブな計算カラムを作ることだね:ALTER TABLE example ADD name_ci AS name COLLATE SQL_Latin1_General_CI_AS;(お好みで調整してね)

ブログにタイプミスがあるよ。最初の行は大文字にする必要がある: > query WHERE name = ‘ABC’ > create an indexed UPPER(name) column ポイントは、インデックス自体がすでに関数が適用されたデータにあるってこと。だから、元のクエリのようにフルスキャンにはならないんだ。もちろん、この特定の例では最初から大文字小文字を区別しない照合を使いたいだけなんだけど、一般的な概念としては有効だよ。

重複を「修正」するためにDISTINCTを使いすぎる どんな時にクエリでDISTINCTを見かけると、すぐにそのクエリを書いた人がデータモデルを十分に理解していないか、集合論を理解していないか、もっと言うとその両方だと思っちゃう。

それ、ほぼいつも私もそう感じるよ。ただ最近、正しい結合があっても、CTE内でDISTINCTを追加することでパフォーマンスが劇的に上がることがあるって学んだんだ。レコードの一意性が保証されていると、クエリプランナーが何らかの最適化をするんじゃないかな。

わからないけど、「どのZIPコードに顧客がいるの?」って知りたいのは普通のことだと思うよ。

それは単に、過剰に正規化されていないスキーマの指標かもしれないね(重複する都市がアドレステーブルに書き込まれないように、わざわざaddresses_citiesテーブルを作る必要があるの?)。

それについては一般化しすぎない方がいいと思う。普段どのクエリを読んでいるかによるんじゃないかな。

私の経験では、データベースの設計に問題があることが多いのは、クエリを書く人のせいと同じくらいです。

IDを取得するためのクエリのように、最大でも1つの結果を返すことを期待してLIMIT 1を追加したら、似たような嫌なことを言われたことがあるよ。でも、大きなテーブル(少なくともSQLite、MySQL、そして多分PostgreSQLでも)では、指定したレコードが見つかった後も、データベースはテーブル全体を検索し続けるんだ。

Hacker Newsで議論の続きを見る