#47 クエリパフォーマンスの定石を試す(1)

結論
クエリパフォーマンスの定石『SELECT * は使わない』はAccessの場合にも当てはまる。「SELECT *」はなるべく使わず、「SELECT フィールド名, フィールド名, ・・・・・」というように必要なフィールドだけを取り出す方が高速である。


クエリ(SQL文)の実行時のパフォーマンスを上げるために一般的に言われていること、すなわち”定石”がAccess(JETデータベースエンジン)でも同様に通じるのかを試す第1弾です。今回は、『SELECT * は使わない』という定石を確認してみました。


SELECT文を書く場合、基本的な構文としては「SELECT フィールド名, フィールド名, ・・・・・」という書き方を使います。しかし、テーブルに保存されているすべてのフィールドを取り出したい場合には、省略形として「SELECT * 」という記述が使えます。
これは、クエリオブジェクトとして作る場合には、デザインビューにおいてフィールドリストから「*」をグリッドに配置すること、もしくはグリッドにフィールドを何も配置しないでクエリプロパティの「全フィールド表示」を”はい”に設定することによって内部生成されるSQL文になります。

この場合、一般的には『「SELECT *」ではすべてのフィールドが取り出されるため、もしそのあとにすべてのフィールドデータを取り扱わない場合には不必要なオーバーヘッドが発生してしまうため、必要なフィールドのみを列挙すべき』と言われているものです。

そこで今回は、「SELECT *」と「SELECT フィールド名, フィールド名, ・・・・・」でどの程度実行時間が違うか、それらを実際に比較することによって定石を検証してみたいと思います。


今回の実験では、総レコード数10万件のテーブルを用意しました。また、”読み込むフィールド数によってパフォーマンスへの影響があるだろう”という前提ですので、少ないフィールド数では「*」と「フィールド名, フィールド名, ・・・・・」の違いが出づらいはずです。そこであえて多めのフィールドを用意しました。長さ20のテキスト型のフィールドを50ケ用意してあります。

そして、実行してみるテストパターンは次の2つです。いずれも場合、テーブルを開いたあと、全レコードをトレースし、6つのフィールドのデータをダミー変数に代入するだけという処理です。処理対象フィールドは、50ケのフィールド位置の影響もあるかもしれませんので、均等にアクセスされるよう等間隔で取るようにしています。
  1. 「SELECT *」でテーブルのレコードセットを開く(内部的には50ケのフィールドが読み込まれることが想定されます)

  2. 「SELECT フィールド名, フィールド名, ・・・・・」というSQL文を記述し、ダミー用の変数に代入するフィールドだけを列挙してレコードセットを開く
それぞれのテストパターンについて、同じ呼び出しをループで10回ずつ行ない、その総処理時間と1回当たりの平均処理時間を比較するものとします。


テスト用のコードは次のようなものです。OpenRecordsetメソッドの引数に与えるSQL文を除いては2つのテストパターンに違いはありません。

Sub QueryPerformance_1()

  Dim dbs As Database
  Dim rst As Recordset
  Dim strSQL As String
  Dim iintLoop As Integer
  Dim strDummy As String

  ts_Watch "処理開始", True

  Set dbs = CurrentDb

  For iintLoop = 1 To 10
    'テストパターン1
    strSQL = "SELECT * FROM tblテスト"
    Set rst = dbs.OpenRecordset(strSQL)
    With rst
      Do Until .EOF
        strDummy = !フィールド01
        strDummy = !フィールド11
        strDummy = !フィールド21
        strDummy = !フィールド31
        strDummy = !フィールド41
        strDummy = !フィールド50
        .MoveNext
      Loop
      .Close
    End With
    ts_Watch "SELECT *"

    'テストパターン2
    strSQL = "SELECT フィールド01, フィールド11, フィールド21, " & _
                      "フィールド31, フィールド41, フィールド50 "
 & _
                      "FROM tblテスト"
    Set rst = dbs.OpenRecordset(strSQL)
    With rst
      Do Until .EOF
        strDummy = !フィールド01
        strDummy = !フィールド11
        strDummy = !フィールド21
        strDummy = !フィールド31
        strDummy = !フィールド41
        strDummy = !フィールド50
        .MoveNext
      Loop
      .Close
    End With
    ts_Watch "SELECT フィールド名"

  Next iintLoop

End Sub


その結果は次のようになりました。
テストパターン 総実行時間(秒) 平均実行時間(秒)
SELECT * 26.26 2.63
SELECT フィールド名 18.57 1.86

ご覧のように、結果は明らかに”定石”通りになっています。フィールド50ケを読み込むか、あるいは6ケだけを読み込むかの違いがその時間差に出ていると思われます。

これらのことから、「SELECT *」はなるべく使わず必要なフィールドだけをSELECTの次に列挙する方が高速であるということがいえます。

ただし、フィールド数が数ケしかないテーブルの場合にはまたその違いは変わってくるかもしれません。おそらく両者の差は縮まるはずです。また、50ケのフィールドがあろうが100ケのフィールドがあろうが、その後処理でそれら全部のフィールドのデータを処理する場合、クエリオブジェクトは使わずそれをVBAのコード上で組み立てるとなると、後者の書き方ではSQL文が長くなってメンテナンスも大変です。よって、テーブルのフィールド総数と読み込み後の処理で使うフィールド数の差が少ない場合、すなわちテーブル上のフィールドのほとんどを必要とする場合には「SELECT *」という書き方もあながち間違いではないように思います。
| Index | Prev | Next |

 

Copyright © T'sWare All rights reserved