Share via


DO's&DONT's #4: やらない方がいいこと - クエリの 条件句 (WHERE や JOIN ON 等) で参照されている列の加工

神谷 雅紀
SQL Server Escalation Engineer

 

パフォーマンスに優れたクエリを書こうとするのであれば、クエリの WHERE 句や結合句で参照されるテーブルの列を関数などで加工してはいけません。

 

悪い例

SELECT * FROM Sales WHERE convert(char(10),EntryDate, 111) = ‘2010/12/01’

SELECT * FROM Sales WHERE FistName + Last Name = @name

 

なぜ?

その列にインデックスがあったとしても、そのインデックスによる行の絞込みが行えないからです。

例えば、上の例の最初のクエリでは、以下の処理が必要です。

  1. テーブルから行を読み取る。
  2. 列 EntryDate の値を convert 関数に渡す。
  3. convert 関数から返された値を定数 ‘2010/12/01’ と比較する。
  4. 比較の結果、一致すれば、その行をクエリ結果へ入れる。一致しなければ 1 へ戻り次の行を処理。

このように、クエリ条件に一致しているかどうかを判定するためには、値を関数に渡す必要があり、関数に渡すためには必ず行を読み取ることが必要になるため、EntryDate がインデックスの先頭列であったとしても、そのインデックスを使用して、読み取り範囲を ‘2010/12/01’ に限定することができず、テーブルの全行を読み取る必要があります。読み取り量が多いということは、処理するデータ量が多いということであり、処理するデータ量が多ければ、処理完了までには時間がかかることになります。つまり、クエリの実行時間が長くなります。

 

どうするか?

クエリが同じ結果を生成し、かつ、列の値を加工しない別の方法を検討します。

例えば、上の例であれば、’2010/12/01 00:00:00.000’ 以上、かつ、’2010/12/02 00:00:00.000’ 未満という条件でも同じ結果を得られるのではないでしょうか。

SELECT * FROM Sales WHERE EntryDate >= '2010/12/01 00:00:00.000' and EntryDate < '2010/12/02 00:00:00.000'

EntryDate を文字列型とし、データは年月日のみを持つようにするという方法も考えられます。方法としてはいろいろと考えられるため、システム全体を考慮して、クエリの書き方で対応するのか、あるいはテーブルスキーマを変更するのか、もしくはその他の方法を用いるのかを検討します。