カテゴリー名: [ADOによるデータベースの中身の把握]
データベースの仮装テーブルに関する情報の取得を取り上げます。
素材となる TestDB.mdb の作成方法については「データベースのTable情報の取得」を参照してください。
ただ、TestDB.mdb には仮装テーブルが一つしか含まれていません。
以下のサイトから Northwind.mdb をダウンロードして、その仮装テーブルをチェックすることをお勧めします。
コンピュータ演習 Excel2000 & Access2000
「標準テーブル」は、実態としてデータをきちんと保持している通常のテーブルです。「実テーブル」と表現する場合もあるようです。
それに対して「仮装テーブル」は、標準テーブルに一定の条件を適用してデータを抽出するものです。
実態としてのデータは持っておらず、抽出条件が保持されているだけです。でも、データベース操作のときに、あたかも標準テーブルであるかのように扱うことができます。
仮装テーブルの場合も、フィールド情報の取得の方法は標準テーブルのときと同じです。
ポイントは、標準テーブルに対して「どんな抽出条件を適用するのか」です。この確認方法がこのページの要点です。
変数CATにADOXのCatalogオブジェクトが代入されているとき、
CAT.Tables
でTableオブジェクトを扱えます。
この集合には、標準テーブルだけでなく仮装テーブルも入っています。といっても、仮装テーブル独自の情報を取り出すことはできません。つまり、仮装テーブルの「抽出条件」を確認できません。
一方、CAT.Views
によってViewオブジェクトを扱うことができます。これには仮装テーブルの情報が入っています。標準テーブルは入りません。
CAT.Views(0)
とか CAT.Views("TableMerge")
のようにして、
番号またはテーブル名をパラメータとして渡すことによって、集合の中の一つのViewオブジェクトを指定できます。
一つのViewオブジェクトについて、まず注目すべきは次のプロパティです。
Tableオブジェクトには Columns プロパティがあってフィールド情報を取り出せます。しかし、Viewオブジェクトには Columns プロパティがありません。
その代わりという訳ではありませんが Command というプロパティがあります。ADOのCommandオブジェクト(ADODB.Command)を指し示しています。
Commandの下には更にいろいろなプロパティがありますが、抽出条件が記録されているのは CommandText です。
TestDB.mdb には TableMerge という仮装テーブルが入っています。
TableA, TableB という二つの実テーブルを結合したものです。
その CommandText は下のとおり。
SELECT TableA.*, TableB.職業, TableB.免許の有無, TableB.生年月日
FROM TableA, TableB
WHERE TableA.ID = TableB.ID;
上の意味は、TableA, TableB の二つのテーブルについて、「ID」が同じなら、それぞれのテーブルから列記してあるフィールドを抽出しなさい、というものです。
TableA.*
は、TableAの全フィールドを指します。
この CommandText をSQL命令として実行した結果が仮装テーブルになる訳です。
TableA, TableB それぞれ100人分のレコードが入っていて、「ID」が一致しないものはありません。
そのため、仮装テーブル TableMerge は、あたかも100人分のレコードが入っているかのように扱えます。
TestDB.mdbについて、仮装テーブルの CommandText を表示する簡単なマクロを掲げてみます。Excel用のVBAマクロです。
Sub Macro1()
Dim DbName As String, DbPath As String, ConnStr As String
Dim CN As ADODB.Connection, CAT As ADOX.Catalog
Dim Vw As Variant
DbName = "TestDB.mdb"
DbPath = ActiveWorkbook.Path & "\" & DbName
ConnStr = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & DbPath & ";"
Set CN = New ADODB.Connection
CN.Open ConnStr
Set CAT = New ADOX.Catalog
CAT.ActiveConnection = CN
For Each Vw In CAT.Views ' Viewを一つずつたどる
MsgBox Vw.Name & vbCrLf & Vw.Command.CommandText
Next
CN.Close
End Sub
仮装テーブルの情報をExcelのワークシートに書き出すVBAマクロを掲げます。
テーブル名と、仮装テーブルの抽出条件(SQL命令文)を書き出します。
1' 仮装テーブルの情報の取得 2Sub Macro1() 3 Dim DbName As String, DbPath As String, ConnStr As String 4 Dim CN As ADODB.Connection, CAT As ADOX.Catalog 5 Dim FSO As Object, Vw As Variant, Ary As Object 6 Dim i As Integer, RowObj As Variant 7 8 Set FSO = CreateObject("Scripting.FileSystemObject") 9 DbName = InputBox("Accessファイルの名前: ", _ 10 "仮装テーブル情報の取得", "TestDB.mdb") 11 If DbName = "" Then Exit Sub 12 CreateObject("WScript.Shell").CurrentDirectory = ThisWorkbook.Path 13 DbPath = FSO.GetAbsolutePathName(DbName) 14 If FSO.FileExists(DbPath) = False Then 15 MsgBox "ファイルがみつかりません: " & DbPath 16 Exit Sub 17 End If 18 19 ConnStr = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & DbPath & ";" 20 Set CN = New ADODB.Connection 21 CN.Open ConnStr 22 Set CAT = New ADOX.Catalog 23 CAT.ActiveConnection = CN 24 25 Set Ary = CreateObject("System.Collections.ArrayList") 26 For Each Vw In CAT.Views ' 仮装テーブルを一つずつたどる 27 Ary.Add Array(Vw.Name, Vw.Command.CommandText) 28 Next 29 CN.Close 30 31 ActiveSheet.UsedRange.Clear 32 For i = 1 To Ary.Count 33 Range(Cells(i, 1), Cells(i, 2)).Value = Ary(i-1) 34 Next 35 Columns("A").ColumnWidth = 20 36 Columns("B").ColumnWidth = 72 37 For Each RowObj In ActiveSheet.UsedRange.Rows 38 RowObj.AutoFit 39 Next 40 ActiveSheet.UsedRange.WrapText = True ' 折り返して全体を表示 41End Sub
先の macro03.txt と同じ処理を行う VBScript を掲げます。
ただし、Excelに書き出すのでなく、ViewInfo.txt というテキストファイルに出力します。
1' 仮装テーブルの情報を取得 2Option Explicit 3Dim FSO, Fobj, DbName, DbPath 4Dim ConnStr, CN, CAT, Vw, Ary, i 5 6Set FSO = CreateObject("Scripting.FileSystemObject") 7DbName = InputBox("Accessファイルの名前: ", _ 8 "仮装テーブル情報の取得", "TestDB.mdb") 9If DbName = "" Then WScript.Quit 10DbPath = FSO.GetAbsolutePathName(DbName) 11If FSO.FileExists(DbPath) = False Then 12 MsgBox "ファイルがみつかりません: " & DbPath 13 WScript.Quit 14End If 15 16ConnStr = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & DbPath & ";" 17Set CN = CreateObject("ADODB.Connection") 18CN.Open ConnStr 19Set CAT = CreateObject("ADOX.Catalog") 20CAT.ActiveConnection = CN 21 22Set Ary = CreateObject("System.Collections.ArrayList") 23For Each Vw In CAT.Views ' 仮装テーブルを一つずつたどる 24 Ary.Add Array(Vw.Name, Vw.Command.CommandText) 25Next 26CN.Close 27Set Fobj = FSO.OpenTextFile("ViewInfo.txt", 2, True) 28For i = 0 To (Ary.Count-1) 29 Fobj.WriteLine Join(Ary(i), vbCrLf) 30Next 31Fobj.Close
先に掲げた VBScript と同じ処理を行う JScript は下のとおり。
1// 仮装テーブルの情報の取得 2var fso, Fobj, dbName, dbPath; 3var connStr, cn, cat, vw, Ary, i, j; 4 5fso = WScript.CreateObject("Scripting.FileSystemObject"); 6dbName = InputBox("Accessファイルの名前: ", 7 "仮装テーブル情報の取得", "TestDB.mdb"); 8if (dbName == "") WScript.Quit(); 9dbPath = fso.GetAbsolutePathName(dbName); 10if (fso.FileExists(dbPath) == false) { 11 var shellObj = WScript.CreateObject("WScript.Shell"); 12 WScript.Echo("ファイルがみつかりません: " + dbPath); 13 while (shellObj.AppActivate("Windows Script Host") != true) { 14 WScript.Sleep(100); 15 } 16 WScript.Quit(); 17} 18 19connStr = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + dbPath + ";"; 20cn = WScript.CreateObject("ADODB.Connection"); 21cn.Open(connStr); 22cat = WScript.CreateObject("ADOX.Catalog"); 23cat.ActiveConnection = cn; 24 25Ary = []; 26for (i=0; i<cat.Views.Count; i++) { // 仮装テーブルを一つずつたどる 27 vw = cat.Views(i); 28 Ary.push([vw.Name, vw.Command.CommandText]); 29} 30cn.Close(); 31Fobj = fso.OpenTextFile("ViewInfo.txt", 2, true); 32for (i = 0; i < Ary.length; i++) { 33 Fobj.WriteLine(Ary[i].join("\n")); 34} 35Fobj.Close(); 36 37// ---------------- 38 39function InputBox(prmpt, ttl, dflt) { 40 var sh = WScript.CreateObject("WScript.Shell"); 41 var tmpFile = sh.Environment("Process").item("TEMP") + "\\MyTest.vbs"; 42 var fso = WScript.CreateObject("Scripting.FileSystemObject"); 43 var cmdStr = "Set FSO = CreateObject(\"Scripting.FileSystemObject\")\n" + 44 "dbName = InputBox(\"" + prmpt + "\", _\n" + 45 "\"" + ttl + "\", \"" + dflt + "\")\n" + 46 "FSO.GetStandardStream(1).Write dbName\n"; 47 var Fobj = fso.OpenTextFile(tmpFile, 2, true); 48 Fobj.Write(cmdStr); 49 Fobj.Close(); 50 execObj = sh.Exec("CScript.exe /Nologo " + tmpFile); 51 while (execObj.Status == 0) { // コマンド実行の終了を待つ 52 WScript.Sleep(100); // 0.1秒待機 53 } 54 var resStr = execObj.StdOut.ReadLine(); 55 fso.DeleteFile(tmpFile); 56 return resStr; 57}
仮装テーブルの「抽出条件」(SQL命令文)を、ExcelのQueryTable機能の下で実行し、その結果をワークシートに取り込みます。
仮装テーブルの中身を知るということでいえば、
select * from TableMerge;
といったSQL命令を実行すれば事足ります。
複雑な抽出条件のSQL命令をわざわざ実行する必要はありません。
ではありますが、ややこしいSQL命令をほんとに実行できるか気になるので試してみました。
結果は「大丈夫」でした。
ただし、SQL命令文の中にダブルクォーテーションがあると正常に実行できないようです。なぜかは分かりません。AccessとExcelの仕様の違いでしょうか。
そこで、ダブルクォーテーションをシングルクォーテーションに変換してみたところ、QueryTableの下でもうまく実行できました。
Northwind.mdb の中の「1997年 売上高」という仮装テーブルのSQL命令文は次のとおりです。ダブルクォーテーションが出てきます。
SELECT DISTINCTROW 商品区分.区分名, 商品.商品名, Sum(CCur([受注明細].[単価]*[数量]*(1-[割引])/100)*100) AS 商品別売上高, "第 " & DatePart("q",[受注日]) & " 四半期" AS 出荷期
FROM (商品区分 INNER JOIN 商品 ON 商品区分.区分コード = 商品.区分コード) INNER JOIN (受注 INNER JOIN 受注明細 ON 受注.受注コード = 受注明細.受注コード) ON 商品.商品コード = 受注明細.商品コード
WHERE (((受注.出荷日) Between #1/1/1997# And #12/31/1997#))
GROUP BY 商品区分.区分名, 商品.商品名, "第 " & DatePart("q",[受注日]) & " 四半期";
数式も含んだ複雑なSQLです。
ですが、ダブルクォーテーションをシングルクォーテーションに変更することで
QueryTableの下で実行できました。
以下、VBAマクロを掲げます。
1' 仮装テーブルをExcelに取り込む(QueryTable利用) 2Sub Macro1() 3 Dim DbName As String, DbPath As String, ConnStr As String, sql As String 4 Dim FSO As Object, Vw As Variant 5 Dim CN As ADODB.Connection, CAT As ADOX.Catalog 6 Dim WSobj As Worksheet, SheetCount As Integer, i As Integer 7 8 Set FSO = CreateObject("Scripting.FileSystemObject") 9 DbName = InputBox("Accessファイルの名前: ", _ 10 "仮装テーブルの取り込み", "TestDB.mdb") 11 If DbName = "" Then Exit Sub 12 CreateObject("WScript.Shell").CurrentDirectory = ThisWorkbook.Path 13 DbPath = FSO.GetAbsolutePathName(DbName) 14 If FSO.FileExists(DbPath) = False Then 15 MsgBox "ファイルがみつかりません: " & DbPath 16 Exit Sub 17 End If 18 19 ConnStr = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & DbPath & ";" 20 Set CN = New ADODB.Connection 21 CN.Open ConnStr 22 Set CAT = New ADOX.Catalog 23 CAT.ActiveConnection = CN 24 25 ConnStr = "ODBC;DSN=MS Access Database;DBQ=" & DbPath 26 SheetCount = 0 27 For Each Vw In CAT.Views ' 仮装テーブルを一つずつたどる 28 sql = Vw.Command.CommandText 29 sql = Replace(sql, """", "'") 30 SheetCount = SheetCount + 1 31 If Worksheets.Count < SheetCount Then ' シート枚数不足 32 Worksheets.Add After:=Worksheets(Worksheets.Count) 33 End If 34 Set WSobj = Worksheets(SheetCount) ' 最後のシートに着目 35 WSobj.UsedRange.Clear ' 念のためシートを全クリア 36 WSobj.Name = Vw.Name ' シート名をテーブル名にする 37 With WSobj.QueryTables.Add(ConnStr, WSobj.Range("A1"), sql) 38 .BackgroundQuery = False ' バックグラウンド処理をしない 39 .Refresh 40 .Delete ' クエリテーブルを削除 41 End With 42 Next 43 CN.Close 44 If (SheetCount > 0) And (Worksheets.Count > SheetCount) Then 45 Application.DisplayAlerts = False ' 警告メッセージを抑制 46 For i = Worksheets.Count To (SheetCount+1) Step -1 47 Worksheets(i).Delete 48 Next 49 Application.DisplayAlerts = True 50 End If 51 Worksheets(1).Activate ' 第1シートをアクティブに 52End Sub
〜 以上 〜