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

  • 質問

  • (※ 2011 年 2 月 24 日に Microsoft SQL Server Japan Support Team Blog に公開した情報のアーカイブです。)

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

    2019年3月28日 11:06
    所有者