SQLでは、ユーザーがフィルターを設定する場合のレポートや検索で動的なフィルタリングが必要なことがよくあります。主なアプローチは以下の通りです:
(@param IS NULL OR フィールド = @param) という構文を使用し、パラメータが指定されていない場合は余分な条件が結果に影響を与えないようにします。この方法は安全ですが、時にはインデックスの最適な使用を妨げることがあります。注文検索用のテンプレート例:
SELECT * FROM Orders WHERE (@CustomerID IS NULL OR CustomerID = @CustomerID) AND (@Status IS NULL OR Status = @Status) AND (@DateFrom IS NULL OR OrderDate >= @DateFrom) AND (@DateTo IS NULL OR OrderDate <= @DateTo);
質問: なぜ、(@param IS NULL OR Column = @param) のアプローチを使用すると、Columnにインデックスがあってもパフォーマンスが悪化することがあるのか?
答え: このテンプレートは、条件に変数が含まれているため、オプティマイザがインデックスを効率的に使用するのを妨げ、クエリがインデックス検索ではなくテーブル全体のスキャンに変わってしまうからです。大量のデータの場合は、これは致命的です。
例:
WHERE (@name IS NULL OR name = @name)
この条件は、単にname = @nameであるのとは異なり、DBMSがインデックスを簡単に使用することを妨げます。
物語
顧客データベースでの検索サービスに「柔軟なフィルター」を(@par IS NULL OR Field = @par)というテンプレートで導入したところ、注文量が増えるにつれて検索速度が秒単位から分単位に低下しました—オプティマイザが具体的な値を特定できなかったため、インデックスが機能しませんでした。
物語
eコマースプロジェクトでは、動的SQLを使ってパラメータのないフィルタリングを行い、値を直接埋め込んでいました。インジェクションによりユーザーは全顧客のリストを取得可能になり、フィルターが脆弱だったため、急遽パスワードを変更しました。
物語
BIレポートはパラメータ化されたテンプレートを使用して構築されていましたが、OR条件がインデックス付スキャンを排除することを考慮していませんでした。百万単位のデータセットでは、レポートがフリーズし、最適な条件と異なるパラメータの組み合わせごとのロジックを用いたフィルタリングに書き換えるまで状況は改善しませんでした。