#35 | 定義域集計関数とSQLの比較 | ||||||||||||||||||
DLookupやDCount、DSumなどの関数は「定義域集計関数」と呼ばれ、一般的には低速な関数といわれています。一方それらの関数は、すべてまったく同等の処理をSQLで行なうことができます。ここでは、それらを実際に比較することによって、本当に定義域集計関数が遅いのか、またSQLによるものとどれくらい違うのかを確認してみたいと思います。
それぞれのテストパターンについて、同じ呼び出しをループで1万回ずつ行ない、その総処理時間を比較するものとします。 Sub Test() 'テストのメインプロシージャ Dim dbs As Database Dim rst As Recordset Dim strSQL As String Dim lngID As Long Dim iintLoop As Integer Set dbs = CurrentDb 'DLookup関数のテスト For iintLoop = 1 To 10000 lngID = DLookup("顧客ID", "顧客マスタ", "会社名='日本商事'") Next iintLoop 'DLookup代替関数のテスト For iintLoop = 1 To 10000 lngID = DLookupTest() Next iintLoop 'SQLによるDLookup代替テスト strSQL = "SELECT 顧客ID FROM 顧客マスタ WHERE 会社名='日本商事'" For iintLoop = 1 To 10000 Set rst = dbs.OpenRecordset(strSQL) lngID = rst!顧客ID rst.Close Next iintLoop 'DCount関数のテスト For iintLoop = 1 To 10000 lngID = DCount("顧客ID", "顧客マスタ", "会社名='日本商事'") Next iintLoop 'DCount代替関数のテスト For iintLoop = 1 To 10000 lngID = DCountTest() Next iintLoop 'SQLによるDCount代替テスト strSQL = "SELECT Count(*) AS RecCnt FROM 顧客マスタ WHERE 会社名='日本商事'" For iintLoop = 1 To 10000 Set rst = dbs.OpenRecordset(strSQL) lngID = rst!RecCnt rst.Close Next iintLoop End Sub Function DLookupTest() As Long 'DLookup代替関数 Dim dbs As Database Dim rst As Recordset Dim strSQL As String Set dbs = CurrentDb strSQL = "SELECT 顧客ID FROM 顧客マスタ WHERE 会社名='日本商事'" Set rst = dbs.OpenRecordset(strSQL) DLookupTest = rst!顧客ID rst.Close End Function Function DCountTest() As Long 'DCount代替関数 Dim dbs As Database Dim rst As Recordset Dim strSQL As String Set dbs = CurrentDb strSQL = "SELECT Count(*) AS RecCnt FROM 顧客マスタ WHERE 会社名='日本商事'" Set rst = dbs.OpenRecordset(strSQL) DCountTest = rst!RecCnt rst.Close End Function メインプロシージャ「Test」で各テストパターンの呼び出しを行ないます。またこのプロシージャ内では、3番目のテストパターン用に、CurrentDb関数を使ってDatabaseオブジェクトを生成しています。この部分の生成時間はテスト結果には含めません。 一方、「DLookupTest」と「DCountTest」の2つのFunctionプロシージャは、それぞれDLookup関数、DCount関数と同等の処理行なうもので、SQL文によってそのレコードセットを生成し、得られた1レコードのフィールド値を返します。これらのプロシージャでは、呼び出されるたびにCurrentDb関数によってDatabaseオブジェクトが生成され、その処理時間もテスト結果に含まれることになります。なお、ここで用いられているSQL文はあらかじめテーブル名やWhere条件が固定されており、あくまでもテスト用に簡略されたもので、厳密にはDLookup関数などの代替とはいえません。定義域集計関数のように、さまざまなテーブル名やWhere条件に対応させるとそれなりに処理が増えますので、正確には実行結果の時間が増える可能性があります。 テストの実行はメインプロシージャ「Test」を1回実行するだけです。そしてその結果は次のようになりました。 ご覧のように、明らかな時間の差が出ています。もっとも高速なのはDatabaseオブジェクトの生成を1回だけ実行した場合のSQLによる代替処理です。それに比べると時間はかなりかかっていますが、2番目に速いのは通常の定義域集計関数でした。定義域集計関数よりは速いのではないかと予想していた独立したプロシージャの代替関数は、もっとも遅いという結果になりました。 これらのことから、確かに定義域集計関数の処理は低速であるが、Databaseオブジェクトを毎回生成するような代替関数を使うよりは高速であるということがいえます。また、Databaseオブジェクトの生成にはかなりのオーバーヘッドがかかるようで、それさえうまく回避できれば、定義域集計関数を使うよりも代替のSQLを使ってレコードセットから結果を得た方がかなり高速に処理できるということも分かります。上記の「Test」プロシージャのように、CurrentDb関数を1回使ったあと、さまざまな定義域集計関数を使いたいようなケースでは、SQL文を使った方法がかなり有効です。 また、どうしてもFunctionプロシージャ化された代替関数を使いたい場合には、上記コードでの「dbs」という変数をPublicな変数として宣言し、一連の処理を実行する前にその値を設定しておくことで、対処可能です。もちろん、各Functionプロシージャ内ではCurrentDb関数は実行せず、そのPublicな「dbs」変数を使うようにします。 なお、今回Where条件として使った「会社名」というフィールドには一切キーは設定してありません。よってそれがキー付きのフィールドであった場合には、当然SQLの実行では速くなることが予想されますし、定義域集計関数も速くなる(もしかしたら遅くなるかも)可能性もありますので、場合によっては差異がなくなるかもしれません。それらはケースバイケースとして別途テストが必要かもしれません。 |
|||||||||||||||||||
|
Copyright © T'sWare All rights reserved |