カテゴリー名: [ADOによるデータベースの中身の把握]
データベース操作のうち、Fieldに関する情報の把握を取り上げます。
素材となる TestDB.mdb の作成方法については「データベースのTable情報の取得」を参照してください。
Accessデータベースの中身を把握するには、Table情報に加えて Field情報も見る必要があります。
「データベースのTable情報の取得」のところでは Tableオブジェクトの Name, Type を見ましたが、他に Columns というプロパティがあります。これを見ると Field情報を知ることができます。
Field情報にはいろいろなものがありますが、基本となるのは次の三つです。
varchar(10)
だと 10 がそれです。これらを知るには下のプロパティを見ます。
TableObject.Columns(0).Name
TableObject.Columns("ID").Type
TableObject.Columns("職業").DefinedSize
フィールド(Column)の指定は、番号または名前のどちらでも行えます。
Columns(0)
とか Columns("職業")
などと書くことができます。
番号は 0 から始まります。
データ型を示す Type は、文字列でなく数値です。おおよそ次のようなものがあります。
数値 | 定数名 | 意味 | SQL表記 |
---|---|---|---|
2 | adSmallInt | 符号付き整数・約+-3万2千以内 | smallint integer2 short |
3 | adInteger | 符号付き整数・約+-21億以内 | int integer integer4 long |
4 | adSingle | 単精度浮動小数点 | real float4 |
5 | adDouble | 倍精度浮動小数点 | float double float8 |
6 | adCurrency | 通貨 | money |
7 | adDate | 日付型 | date time datetime smalldatetime |
8 | adBSTR | 文字列値 | □□ |
9 | adIDispatch | COMオブジェクトのIDispatchへのポインタ | □□ |
10 | adError | 32ビットエラーコード | □□ |
11 | adBoolean | Yes|No型 | bit yesno logical |
12 | adVariant | オートメーションバリアント型 | □□ |
13 | adIUnknown | COMオブジェクトのIUnknownインターフェイスへのポインタ | □□ |
14 | adDecimal | 固定精度・スケールの正確な数値 | □□ |
16 | adTinyInt | 符号付き整数・-128→+127 | □□ |
17 | adUnsignedTinyInt | 符号なし整数・0→255 | tinyint |
18 | adUnsignedSmallInt | 符号なし整数・約6万5千以下 | □□ |
19 | adUnsignedInt | 符号なし整数・約42億以下 | □□ |
20 | adBigInt | 符号付き整数・特大 | □□ |
21 | adUnsignedBigInt | 符号なし整数・特大 | □□ |
64 | adFileTime | 時間・1601年1月1日からの時間 | □□ |
72 | adGUID | 固有のグローバル識別子GUID | guid |
128 | adBinary | バイナリ | □□ |
129 | adChar | 文字列値 | □□ |
130 | adWChar | 固定長文字列・最長255桁 | char nchar |
131 | adNumeric | 固定精度・スケールの正確な数値 | decimal dec numeric |
132 | adUserDefined | ユーザー定義変数 | □□ |
133 | adDBDate | 日付・yyyymmdd | □□ |
134 | adDBTime | 時刻・hhmmss | □□ |
135 | adDBTimeStamp | 日付時刻・yyyymmddhhmmss+α | □□ |
136 | adChapter | 子行セットの行を識別する4バイトチャプタ値 | □□ |
138 | adPropVariant | オートメーションPROPVARIANT | □□ |
139 | adVarNumeric | 数値 | □□ |
200 | adVarChar | 文字列値 | □□ |
201 | adLongVarChar | 長文字列 | □□ |
202 | adVarWChar | 可変長文字列・最長255桁 | varchar nvarchar |
203 | adLongVarWChar | 長文字列 | longtext memo note |
204 | adVarBinary | バイナリ型・最長510桁 | binary varbinary |
205 | adLongVarBinary | 長バイナリ・OLEオブジェクト型 | longbinary image general |
SQL表記が「□□」になっているのは、該当する表記が分からなかったものです。
これらは、該当の表記がないような気がしますが、筆者が分からなかっただけかもしれません。
Northwind.mdb で使われているデータ型は、すべて上の表のSQL表記で示すことができます。
フィールドの最大桁数は、SQL表記でいうと varchar(10)
の文字列型の場合の長さ 10 のことです。varcharの場合は 255以下で指定します。
この数は、文字数を表します。半角も全角も同じ1文字としてカウントします。バイト数(半角文字数)ではありません。
短いバイナリデータを binary(128)
と指定したときは、最大桁数が 128 となります。510桁(つまり 510バイト)以内で指定します。
最大桁数を超えるデータを書き込もうとしても、エラーが発生します。
あと、真偽(True, False)を示すデータ型のサイズは、なぜか 2 です。
といっても、SQL表記ではこの桁数を書いたりはしません。2 という値は気にする必要がないとおもいます。
そのほか、多くのデータ型は、サイズが 0 です。
長い文字列の longtext
、長いバイナリの longbinary
、
日付型の date
、数値を示す int, float, money
など、いずれも最大桁数が 0 です。
定数名 adNumeric(固定精度・スケールの正確な数値)のデータ型の場合は、次のプロパティが値を持ちます。なお、最大桁数(DefinedSize)は 0 です。
SQL表記で decimal(20, 4)
と書くと、Precision=20, NumericScale=4 となります。
Precision の 20 は、小数部を含む数値全体の最大桁数なので、
整数部分の桁数は 20 - 4 = 16 程度となります。
フィールドに関する情報は様々ありますが、「デフォルト値」と「空欄禁止」について見てもみます。
「デフォルト値」は、入力が省略されたときにセットされる値です。
SQL記述で 職業 varchar(20) default 無職
とフィールドを定義すると、
データベース操作の段階で「職業」の欄に何も書かない場合、
「無職」が自動的に書き込まれます。
「デフォルト値」を見るには次のプロパティを参照します。
TableObject.Columns("職業").Properties("Default").Value
「空欄禁止」は、必ず何かを書き込まなければならないとの指定です。
SQL命令では not null
を書いて指定します。たとえば
職業 varchar(20) not null
と定義すると、「職業」というフィールドが空欄禁止になります。
データベース操作のときに「職業」の入力を省略するとエラーになります。
フィールドが「空欄禁止」になっているかどうかを知るには、次のプロパティを見ます。
TableObject.Columns("職業").Properties("NULLable").Value
上のプロパティが False であれば「空欄禁止」です。
True になっていれば「空欄許可」です。
データベース操作の段階で入力を省略すると、自動的に番号が入力されるフィールドがあります。オートナンバーのフィールドです。
ID(識別番号)のフィールドがオートナンバーになっている場合があります。
AccessデータベースのSQL表記のケースでいうと、ID int
と書くところを
ID counter(10,5)
のように書きます。すると、「ID」というフィールドがオートナンバー形式になります。
この場合、10, 15, 20, 25, 30, …… と進んでいきます。
counter(10,5)
の 10 が初期値、5 が増分値です。
オートナンバーに関するプロパティは継ぎの三つです。
TableObject.Columns("ID").Properties("Autoincrement").Value → True|False
TableObject.Columns("ID").Properties("Seed").Value → 初期値
TableObject.Columns("ID").Properties("Increment").Value → 増分値
Properties("Autoincrement").Value
が True であれば該当のフィールドがオートナンバー型です。
Properties("Seed").Value
の初期値は、データベースにレコードを追加する度に増えていきます。テーブルを定義したときは 1 だったとしても、レコードを追加すると 2, 3, 4, …… と増えていきます。
増分値は、定義の際に省略すると 1 になります。counter(10)
だと、10, 11, 12, 13, …… と進みます。
データベースのフィールド情報を出力する VBAマクロを掲げます。Excel用のマクロです。ADO, ADOX への参照設定が行われているとの前提で書かれています。
Macro1 を実行するとデータベース名を入力する場面になります。適当に入力してエンターキーを押します。
すると、ワークシートにテーブルめいとそのフィールド情報が書き出されます。
書き出されるフィールド情報は下の3種類です。
SQL表記の中には primary key
あるいは unique
が出てくる場合があります。
これらは、インデックス情報と関連します。それについては、このページの最後の方で簡単に触れます。
1' Accessデータベースのフィールド情報の取得 2Sub Macro1() 3 Dim DbName As String, DbPath As String, ConnStr As String 4 Dim FSO As Object, TypeCst As Object, TypeSql As Object 5 Dim CN As ADODB.Connection, CAT As ADOX.Catalog 6 Dim Tbl As Variant, FldName As Variant, Col As Object 7 Dim TypeStr As String, IdxHash As Object, Ary As Object, i As Integer 8 9 Set FSO = CreateObject("Scripting.FileSystemObject") 10 DbName = InputBox("Accessファイルの名前: ", _ 11 "Accessフィールド情報取得", "TestDB.mdb") 12 If DbName = "" Then Exit Sub 13 CreateObject("WScript.Shell").CurrentDirectory = ThisWorkbook.Path 14 DbPath = FSO.GetAbsolutePathName(DbName) 15 If FSO.FileExists(DbPath) = False Then 16 MsgBox "ファイルがみつかりません: " & DbPath 17 Exit Sub 18 End If 19 20 ConnStr = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & DbPath & ";" 21 Set CN = New ADODB.Connection 22 CN.Open ConnStr 23 Set CAT = New ADOX.Catalog 24 CAT.ActiveConnection = CN 25 Set TypeCst = TypeCstHash() ' データ型:番号と定数名の関連付け 26 Set TypeSql = TypeSqlHash() ' データ型:番号とSQL表記の関連付け 27 28 Set Ary = CreateObject("System.Collections.ArrayList") 29 For Each Tbl In CAT.Tables ' テーブルを一つずつたどる 30 If (Tbl.Type = "TABLE") Or (Tbl.Type = "VIEW") Then 31 FldName = GetFieldName(CN, Tbl.Name) ' フィールド名一覧を取得 32 Ary.Add Array(Tbl.Name, Tbl.Type) 33 Set IdxHash = IndexHash(Tbl) 34 For i = 0 To UBound(FldName) ' フィールドを一つずつ 35 Set Col = Tbl.Columns(FldName(i)) 36 TypeStr = FieldSqlStr(Col, TypeSql) 37 If IdxHash.Exists(Col.Name) Then 38 TypeStr = TypeStr & " " & IdxHash(Col.Name) 39 End If 40 Ary.Add Array("", Col.Name, TypeCst(Col.Type), TypeStr) 41 Next 42 Ary.Add Array("") 43 End If 44 Next 45 CN.Close 46 47 ActiveSheet.UsedRange.Clear 48 For i = 0 To (Ary.Count-1) 49 Range(Cells(i+1, 1), Cells(i+1, UBound(Ary(i))+1)).Value = Ary(i) 50 Next 51 For i = 1 To ActiveSheet.UsedRange.Columns.Count 52 ActiveSheet.UsedRange.Columns(i).AutoFit 53 Next 54End Sub 55 56 (以下、関数部分は省略)
配列操作にかかわる System.Collections.ArrayList
については、下のサイトが分かりやすく参考になります。
実験記録 No.02 : VBSで動的配列(ArrayList)を使う
フィールド情報を取り出すのに TableObject.Columns
を使いますが、このプロパティは、フィールドの順番を保持していません。
「ID、性別、身長、体重」の順番で定義したのに、TableObject.Columns
を一つずつ取り出すと、「ID、体重、性別、身長」の順番になる、といった具合です。
これだと不都合なので、データベースのレコードのうち、第1レコードだけをチェックし、フィールドの名前を取り出します。こちらは、定義したとおりの順番です。
この処理をやっているのが関数 GetFieldName(CN, TblName)
です。
この関数は、フィールド名を配列に入れて返します。
該当部分を掲げます。
1Function GetFieldName(CN, ByVal TblName) ' フィールド名の取得 2 Dim RS As ADODB.Recordset, FldName() As Variant 3 Dim FldCount As Integer, i As Integer 4 TblName = "[" & TblName & "]" 5 Set RS = New ADODB.Recordset 6 RS.Open TblName,CN,0,1,2 7 FldCount = RS.Fields.Count 8 ReDim FldName(FldCount-1) 9 For i = 0 To (FldCount-1) 10 FldName(i) = RS.Fields(i).Name 11 Next 12 RS.Close 13 Set RS = Nothing 14 GetFieldName = FldName 15End Function
関数 FieldSqlStr(Col, TypeSql)
は、フィールド情報をSQL表記にした文字列を返します。
第2パラメータの TypeSql は、Dictionaryオブジェクトといわれるものです。
プログラムの別の箇所で、整数値を文字列に変換するハッシュテーブルとして定義しています。
TypeSql(5)
が "float"
という文字列となり、
TypeSql(200)
なら "varchar"
という文字列です。
これだけだと、最大桁数、デフォルト値、空欄禁止、オートナンバー型かどうかといった情報がないので、関数 FieldSqlStr の中でそうした情報を付け加えています。
VBAマクロの関連する部分を掲げます。
1Function FieldSqlStr(Col, TypeSql) ' フィールド情報をSQL表記に 2 Dim TypeStr As String, Val As Variant 3 If Col.Properties("Autoincrement").Value = True Then 4 TypeStr = "counter(" & Col.Properties("Seed").Value & _ 5 "," & Col.Properties("Increment").Value & ")" 6 Else 7 TypeStr = TypeSql(Col.Type) 8 End If 9 If (Col.DefinedSize > 0) And (Col.Type <> adBoolean) Then _ 10 TypeStr = TypeStr & "(" & Col.DefinedSize & ")" 11 If Col.Type = adNumeric Then _ 12 TypeStr = TypeStr & "(" & Col.Precision & ", " & _ 13 Col.NumericScale & ")" 14 Val = Col.Properties("Default").Value 15 If Val <> "" Then 16 If InStr(Val, " ") Then Val = "[" & Val & "]" 17 TypeStr = TypeStr & " DEFAULT " & val 18 End If 19 Val = Col.Properties("NULLable").Value 20 If Val = False Then 21 TypeStr = TypeStr & " NOT NULL" 22 End If 23 FieldSqlStr = TypeStr 24End Function 25 26Function TypeSqlHash() 27 Dim TypeSql As Object 28 Set TypeSql = CreateObject("Scripting.Dictionary") 29 TypeSql.Add 2, "SmallInt" 30 (中略) 31 Set TypeSqlHash = TypeSql 32End Function
Accessデータベースのフィールド情報を FieldInfo.txt というテキストファイルに書き出すVBScriptを掲げます。
処理の手順は先の macro02.txt と同じです。
ADO用の定数を定義したファイル constants_ad.vbs を読み込む形になっています。
constants_ad.vbs の中で、次の二つの関数も定義しています。
上のどちらも Dictionaryオブジェクト(ハッシュ)を返します。
メイン部分のみ掲げます。関数の部分は、VBAマクロとほぼ共通です。
1' Accessデータベースのフィールド情報を取得 2Option Explicit 3Dim FSO, Fobj, DbName, DbPath, TypeCst, TypeSql 4Dim ConnStr, CN, CAT, Tbl, FldName, Col, Ary, TypeStr, IdxHash, i 5Include "constants_ad.vbs" 6 7Set FSO = CreateObject("Scripting.FileSystemObject") 8DbName = InputBox("Accessファイルの名前: ", _ 9 "Accessフィールド情報取得", "TestDB.mdb") 10If DbName = "" Then WScript.Quit 11DbPath = FSO.GetAbsolutePathName(DbName) 12If FSO.FileExists(DbPath) = False Then 13 MsgBox "ファイルがみつかりません: " & DbPath 14 WScript.Quit 15End If 16 17ConnStr = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & DbPath & ";" 18Set CN = CreateObject("ADODB.Connection") 19CN.Open ConnStr 20Set CAT = CreateObject("ADOX.Catalog") 21CAT.ActiveConnection = CN 22Set TypeCst = TypeCstHash() ' データ型:番号と定数名の関連付け 23Set TypeSql = TypeSqlHash() ' データ型:番号とSQL表記の関連付け 24 25Set Ary = CreateObject("System.Collections.ArrayList") 26For Each Tbl In CAT.Tables ' テーブルを一つずつたどる 27 If (Tbl.Type = "TABLE") Or (Tbl.Type = "VIEW") Then 28 FldName = GetFieldName(CN, Tbl.Name) ' フィールド名一覧を取得 29 Ary.Add Array("*" & Tbl.Name, Tbl.Type) 30 Set IdxHash = IndexHash(Tbl) 31 For i = 0 To UBound(FldName) ' フィールドを一つずつ 32 Set Col = Tbl.Columns(FldName(i)) 33 TypeStr = FieldSqlStr(Col, TypeSql) 34 If IdxHash.Exists(Col.Name) Then 35 TypeStr = TypeStr & " " & IdxHash(Col.Name) 36 End If 37 Ary.Add Array("", Col.Name, TypeCst(Col.Type), TypeStr) 38 Next 39 Ary.Add Array("") 40 End If 41Next 42CN.Close 43Set Fobj = FSO.OpenTextFile("FieldInfo.txt", 2, True) 44For i = 0 To (Ary.Count-1) 45 Fobj.WriteLine Join(Ary(i), vbTab) 46Next 47Fobj.Close 48 49 (以下、後略)
先の vovTBL02.vbs と同じ処理を行う JScript を掲げます。
constants_ad.js を読み込む形になっています。
やはり、メイン部分だけを掲げます。関数については Program Page を参照して下さい。
1// Accessデータベースのフィールド情報の取得 2var fso, Fobj, dbName, dbPath, typeCst, typeSql; 3var connStr, cn, cat, tbl, fldName, idxHash, col, Ary, i, j; 4eval(ReadFile("constants_ad.js")); 5 6fso = WScript.CreateObject("Scripting.FileSystemObject"); 7dbName = InputBox("Accessファイルの名前: ", 8 "Accessフィールド情報取得", "TestDB.mdb"); 9if (dbName == "") WScript.Quit(); 10dbPath = fso.GetAbsolutePathName(dbName); 11if (fso.FileExists(dbPath) == false) { 12 var shellObj = WScript.CreateObject("WScript.Shell"); 13 WScript.Echo("ファイルがみつかりません: " + dbPath); 14 while (shellObj.AppActivate("Windows Script Host") != true) { 15 WScript.Sleep(100); 16 } 17 WScript.Quit(); 18} 19 20connStr = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + dbPath + ";"; 21cn = WScript.CreateObject("ADODB.Connection"); 22cn.Open(connStr); 23cat = WScript.CreateObject("ADOX.Catalog"); 24cat.ActiveConnection = cn; 25typeCst = TypeCstHash(); // データ型:番号と定数名の関連付け 26typeSql = TypeSqlHash(); // データ型:番号とSQL表記の関連付け 27 28Ary = []; 29for (i=0; i<cat.Tables.Count; i++) { // テーブルを一つずつたどる 30 tbl = cat.Tables(i); 31 if (tbl.Type == "TABLE" || tbl.Type == "VIEW") { 32 Ary.push(["*" + tbl.Name, tbl.Type]); 33 fldName = GetFieldName(cn, tbl.Name); // フィールド名一覧を取得 34 idxHash = IndexHash(tbl); 35 for (j=0; j<fldName.length; j++) { // フィールドを一つずつ 36 col = tbl.Columns(fldName[j]); 37 typeStr = FieldSqlStr(col, typeSql); 38 if (idxHash[col.Name] != null) 39 typeStr = typeStr + " " + idxHash[col.Name]; 40 Ary.push(["", col.Name, typeCst[col.Type], typeStr]); 41 } 42 Ary.push([""]); 43 } 44} 45cn.Close(); 46Fobj = fso.OpenTextFile("FieldInfo.txt", 2, true); 47for (i = 0; i < Ary.length; i++) { 48 Fobj.WriteLine(Ary[i].join("\t")); 49} 50Fobj.Close(); 51 52 (以下、省略)
インデックス(索引)は、検索を素早く行うために設けられるものです。
何万件、何十万件と登録されているデータベースに対して、いつも先頭から最後に向かって素直に検索していたのでは、最後の方のデータを取り出すのに時間がかかってしようがありません。
そこで索引を設けて効率的に検索できるようにします。
といっても、インデックスは、データベースシステムが自動的に作成・管理してくれるので、ユーザー側が意識して作成する必要はありません。
ここではインデックスについて説明するというよりも、フィールド情報との関連で言及するだけなので、主キー(primary key)、重複禁止(unique)に関して簡単に述べます。
主キーは、一つのレコードを取り出すのに手がかりとするメインのフィールドのことです。
各々のレコードにID番号(識別番号)を割り当てて主キーにするというのは、よくある手です。
テーブルを定義するときに ID int primary key
とすれば「ID」が主キーになります。
数あるレコードの中から特定の一つのレコードを取り出す手がかりですから、他のレコードの同じ値がセットされていると不都合です。
となると、レコードを登録する際に「重複したものがないかどうか」をチェックしなければなりません。つまり検索が必要になります。
そこで、データベースシステムは、インデックスを設けて、主キーに指定されたフィールドをそのインデックスに登録します。それにより暗黙のうちに行う検索を効率化します。
主キーにするわけではなくても、同じフィールドに同じ値のものがあると不都合だという場合があります。
会員番号、発注コード、契約識別番号等々、入力の際に謝って他と同じ値を入れてしまうと、後でトラブルになります。
そうしたフィールドは、重複禁止の unique にします。
そうしておけば、重複したものがあるとエラーが発生して登録できません。トラブルを未然に防ぐことになります。
SQL表記では定義時に 発注コード int unique
のように記述します。
Accessデータベースのインデックス情報は、TableObject.Indexes
で参照できます。
一つのテーブルに複数のインデックスがある場合があるので Indexes です。
TableObject.Indexes(0)
とか TableObject.Indexes(1)
として一つのインデックスオブジェクトを参照することができます。
TableObject.Indexes(0).PrimaryKey
が True であれば、第1のインデックスが主キーに関するものであるということになります。
TableObject.Indexes(1).Unique
が True であれば、第2のインデックスが重複禁止に関するものだということです。
でも、このインデックスがどのフィールドに関するものなのかがまだ分かりません。
第1のインデックスオブジェクトがどのフィールドに関連しているかを見るには
TableObject.Indexes(0).Columns
を参照します。
ここで出てくる Columns も複数形であることから推測されるように、一つのフィールドだけに関連している訳ではありません。一つだけかもしれないけど、複数の可能性もあります。
面倒ではありますが、Columns を一つずつ見ていく必要があります。
TableObject.Indexes(0).Columns(0).Name
に "ID"
という文字列が代入されていれば、「ID」というフィールドが主キーだと分かります。
なんともややこしいですが、こんなふうにして確認するしかないようです。
ところで、重複禁止の方はともかく、二つ以上のフィールドが主キーになっているというケースがあるのか、とおもわれるかもしれません。
実は、あります。Northwind.mdb にも一つのテーブルにおいて二つの主キーが指定されているものがあります。
数あるレコードから一つのレコードを取り出す手がかり(フィールド)が、一つだけでなく二つあるわけです。珍しいとはおもいますが。
ともあれ、あるフィールドが主キーかどうか、重複禁止かどうかを見るには、
Indexes の下にぶら下がっている Columns を一つずつチェックする必要があります。
こうした構造になっているインデックス情報を、容易に参照できるハッシュの形にまとめるのが関数 IndexHash(Tbl)
です。
VBAの記述でいうと、Set IdxHash = IndexHash(Tbl)
とすると、
IdxHash("ID")
が "primary key"
という文字列を保持する、といった具合になります。
あるいは、IdxHash("発注コード")
が "unique"
を保持するといった具合です。
以下、関数 IndexHash を掲げます。
1Function IndexHash(Tbl) ' インデックス情報の取得 2 Dim IdxHash As Object, Idx As Variant, Str As String, Col As Variant 3 Set IdxHash = CreateObject("Scripting.Dictionary") 4 For Each Idx In Tbl.Indexes 5 Str = "" 6 If Idx.PrimaryKey Then Str = "primary key" 7 If (Str = "") And Idx.Unique Then Str = "unique" 8 If Str <> "" Then 9 For Each Col In Idx.Columns 10 If IdxHash.Exists(Col.Name) = False Then _ 11 IdxHash.Add Col.Name, Str 12 Next 13 End If 14 Next 15 Set IndexHash = IdxHash 16End Function
〜 以上 〜