#10 2つのテーブルを比較する

アプリケーション開発中のテストでは、ある処理によって出力されたレコードと、そのコードをアレンジしただけの、本来同じ結果が出されるべき処理によって出力されたレコードとが同じかどうか確認したい場合があります。そのテスト方法としては、まず変更前の処理結果であるテーブルを別名でコピーしておき、続いて変更後の処理を実行、両者のテーブルを比較するといった方法があると思います。そのような比較処理を手助けするのがT'sKitの「テーブル/クエリー比較」ツールです。ここではこのツールの内容について説明したいと思います。
 
ず、2つのテーブルが同じであると判断する基準を整理しておきましょう。
  1. フィールド数が同じである。
  2. フィールドの順序が同じである。
  3. 個々のフィールド定義が同じである。
  4. レコード数が同じである。
  5. すべてのレコードのデータ内容が同じである。
T'sKitではこのうち3番目の比較は行っていません。フィールドの定義項目としては、テキスト型のフィールドサイズや書式、定型入力、既定値、入力規則といったものがありますが、厳密に「2つのテーブルが同じである」というためにはもちろんこのチェックは欠かせません。

例えば、処理の中でデータ定義クエリーなどを使ってテーブルを作成しているような場合には、そのクエリーが修正前のクエリーと同じテーブルを作成しているか、それらのフィールド定義の内容も比べてみることが必要となります。しかし、T'sKitの「テーブル/クエリー比較」ツールを使用する場面としては、基本的にデータを生成するVBAコードの修正前後の比較を想定していますので、"故意にテーブル構造が変えられることはない"、という前提で、ここまでの比較には踏み込んでいません。

それでは、上記の1、2、4、5の判断基準について、1つずつコードを説明していきたいと思います。
初に、変数の宣言を行い、続いてすべての比較項目に先立って2つのテーブルを開きます。ここでは"テーブル1"と"テーブル2"という名前の2つのテーブルを比較してみます。実際にはこれらはテーブルを比較するプロシージャの引数にするべきものです。
Dim dbs As Database
Dim rst1 As Recordset, rst2 As Recordset
Dim lngRecMax1 As Long, lngRecMax2 As Long
Dim lngReadRecords As Long
Dim intFldCount As Integer

Set dbs = CurrentDb
Set rst1 = dbs.OpenRecordset("テーブル1")
Set rst2 = dbs.OpenRecordset("テーブル2")
れでは最初の比較項目として「フィールド数の比較」を行ってみましょう。これを最初に比較するのは、コードを見れば分かるように、Recordset オブジェクトがすでに開かれている状態では [Fields]オブジェクト の [Count]プロパティ を見ることによって直ちに比較することができるからです。複数のフィールドを調べたり複数のレコードを調べたりする必要がありませんので、最も速くテーブル間の違いを検出することができます。これを最初に比較することによって2つのテーブルに違いがあった場合にいち早くそれを知らせることができるわけです。


If rst1.Fields.Count <> rst2.Fields.Count Then
  Beep
  MsgBox "2つのテーブル/クエリーは構造(フィールドの数)が異なります!", _
          vbOKOnly + vbInformation
  GoSub AllClose
  'このサブルーチンはテーブル比較のために開いたすべてのテーブルを閉じるものです
  Exit Sub
End If

続いて「フィールド名の比較」を行います。フィールド名もフィールド定義の1つですので、このツールの利用前提からすれば不要なのですが、次のコードのように比較することによって、フィールドの順序も合わせて比較できますので、T'sKitでは一応確認を行っています。

[Fields]オブジェクト の [Count]プロパティ は、そのテーブルのフィールド数を取得するものですが、最初のフィールドは"0"から数え始めますので、0 〜 (rst1.Fields.Count - 1) のループによって各フィールドを先頭から順番に参照していきます。そして、"rst1.Fields(intFldCount).Name" がintFldCount番目のフィールドの[名前]を返しますので、両者の比較を行い、違いがあればメッセージを表示します。

For intFldCount = 0 To rst1.Fields.Count - 1
  If rst1.Fields(intFldCount).Name <> _
        rst2.Fields(intFldCount).Name Then
    Beep
    MsgBox("2つのテーブル/クエリーは構造(フィールド名)が異なります!", _
            vbOKOnly + vbInformation
    GoSub AllClose
    Exit Sub
  End If
Next intFldCount
いて、各レコードにアクセスしてレコードの比較を行っていきます。まず最初は「レコード数の比較」です。

lngRecMax1 = tscl_RecCount(rst1)
lngRecMax2 = tscl_RecCount(rst2)
If lngRecMax1 <> lngRecMax2 Then
  Beep
  MsgBox "2つのテーブル/クエリーはレコード数が異なります!" & vbCrLf _
          "テーブル1 → " & lngRecMax1 & " レコード" & vbCrLf & _
          "テーブル2 → " & lngRecMax2 & " レコード", _
          vbOKOnly + vbInformation
  GoSub AllClose
  Exit Sub
End If

このコードで呼び出されている "tscl_RecCount" Functionプロシージャは、引数として与えられたRecordsetオブジェクトのレコード数を返します。レコード数を得るに[RecordCount]プロパティ値を参照しますが、このプロパティはあくまでもアクセスされたレコード数を返しますので、いったん最後のレコードまで移動してからその値を取得するようにします。

Public Function tscl_RecCount(rst As Recordset) As Long
  On Error Resume Next
  With rst
    .MoveLast
    tscl_RecCount = .RecordCount
    .MoveFirst
  End With

End Function

※RecordsetオブジェクトがOpenRecordsetで開かれた直後の[RecordCount]プロパティ値は、"1" となります。ただし、レコードがそのテーブルやクエリーに1つも無い場合は "0" を返しますので、この応用として、Recordsetオブジェクトを開いた直後に[RecordCount]プロパティを調べることによって、レコードの有無を確認できます。これは特に抽出条件が含まれるクエリーで有用と思います。
後にすべてのレコードの「データ内容の比較」を行います。すべてのデータを比較するということは、"すべてのレコードについてすべてのフィールドの値を比較する"ということですので、まず外側に全レコードを読み込むループを作り、その内側にいわゆる入れ子状に全フィールド値を読み込むループを作ります。すでにそのテーブルのレコード数は変数 lngRecMax1 として得られていますので、それを外側のループの上限とします。また、内側のループ構造は各フィールドの名前を取得した場合と同じです。フィールド名があらかじめ分かっている場合には rst!フィールド名 のような構文でフィールドの値を得ることができますが、ここではさまざまなテーブルを比較できるようにする必要があるため、固定的なフィールド名は使用できません。そこでここでは、[Value]プロパティを使ってそのフィールドに格納された値を取得し、両者のテーブルを比較していきます。コードは次のようになります。

For lngReadRecords = 1 To lngRecMax1
  For intFldCount = 0 To rst1.Fields.Count - 1
    If rst1.Fields(intFldCount).Value <> rst2.Fields(intFldCount).Value Then
      Beep
      MsgBox "2つのテーブル/クエリーに違いが見つかりました!" & vbCrLf & _
              "レコード番号 → " & lngReadRecords & vbCrLf & _
              "フィールド名 → " & rst1.Fields(intFldCount).Name & vbCrLf & _
              "テーブル1 → " & rst1.Fields(intFldCount).Value & vbCrLf & _
              "テーブル2 → " & rst2.Fields(intFldCount).Value, _
              vbOKOnly + vbInformation
      GoSub AllClose
      Exit Sub
    End If
  Next intFldCount
  rst1.MoveNext: rst2.MoveNext
Next lngReadRecords
| Index | Prev | Next |

 

Copyright © T'sWare All rights reserved