DAOでAccess・Excel・CSVへ接続・操作
- 1.データベースへの接続方法
- 2.DAOの文法について
- 2ー1.DAOオブジェクト生成
- 2ー2.データベース接続
- 2ー3.データベースとのデータやり取り
- 2ー3ー1.OpenRecordsetメソッド
- 2ー3ー2.Excecuteメソッド
- 2ー3ー3.パラメータの組み合わせについて
- 2ー3ー3ー1.Accessとしての組み合わせ
- 2ー3ー3ー2.Excel VBAとしてのTypeとOptionsパラメータの組み合わせ
- 2ー3ー3ー3.Excel VBAとしてのLockEditパラメータの組み合わせ
- 3.コード例
- 3ー1.テーブル名を指定する場合
- 3ー1ー1.Accessデータの場合
- 3ー1ー2.Excelデータの場合
- 3ー1ー2ー1.Select相当
- 3ー1ー2ー2.Insert、Update相当
- 3ー1ー3.CSVデータの場合
- 3ー2.SQL文を指定する場合
- 3ー2ー1.Accessデータの場合
- 3ー2ー1ー1.Select文
- 3ー2ー1-2.Insert・Update・Delete文の場合
- 3ー2ー2.Excelデータの場合
- 3ー2ー2ー1.Select文
- 3ー2ー2-2.Insert・Update文の場合
- 3ー2ー3.CSVデータの場合
- 3ー2ー3ー1.Select文
- 3ー2ー3-2.Insert文の場合
- 3ー3.データを一括で処理
- 4.Accessデータの作成について
- 4ー1.Accessファイルの作成
- 4ー2.Accessテーブルの作成
- 4ー3.Accessレコードの作成
- アプリ実例・関連する項目
- サンプルファイル
1.データベースへの接続方法
アプリケーション(今回はExcel)から各種データベースに接続・操作する方式には、主に「ODBC」「DAO」「ADO」の3種あります。技術としての順序は、(古い)ODBC → DAO → ADO(新しい) となります。世の中には数多くのデータベースがありますが、今回の対象はExcelから(ほぼ無料で)使えるデータベースとして「Microsoft Access」「Excelのワークシートを活用したデータベース」「CSVなどのテキストファイルをデータベースにしたもの」の3つとしました。図01は、アプリ(今回ExcelのVBA等)から接続するルートとしてどのようなものがあるかをまとめたものです。
図01
図01の一番左のルート(緑色の線)は「DAO(Data Access Objects)」です。その名前の通り「Access標準」のものですが、Excel・CSV等にも接続可能ですし、今回は対象としていませんがSQL_Server等にも使用できるようです。
一番右のルート(青色の線)は「ODBC(Open DataBase Connectivity)」です。Windowsが管理しているODBCドライバーを使用するため汎用性は高く、C言語などからは色々操作できるようですが、Excelに限ってみると「データの取得(Select文)」のみが可能なようです(Insert等の操作が可能か、までは調べきれませんでした)。
中央のルート(赤色の線)は「ADO(ActiveX Data Object)」です。このADO方式では、データベースに対応したプロバイダーを指定しますが、JET・ACE対応プロバイダーを使いJET・ACEエンジンを経由して、Microsoft系のデータベースを操作する事が可能です。
また、ODBCに対応するプロバイダー(MSDASQL)も用意されているため、ODBCドライバーを使用して多くのデータベースに対応することが可能です。
インターネットで「Excel~データベース」を調べると、多くのサイトがこのADO方式を説明しています。
図01のルートを辿ってみても分かるように、ADOは単独で動いている訳では無く、ODBCやDAOといった旧方式も活用した形で成立しているようです。
今回の項では、DAO方式(図02では、黒線のルート)について説明していきます。
アプリ(今回はExcel)からAccessデータベースへのルートは①、Excelシート上のデータベースへのルートは②、CSV等のテキストファイルへのルートは③と、いずれもJET・ACEエンジンを使います。
図02
DAO以外の接続・操作については、以下を参照下さい。
・DAOでAccess・Excel・CSVへ接続・操作 ←今回
・ADOでAccess・Excel・CSVへ接続・操作(JET・ACEプロバイダ編)
・ADOでAccess・Excel・CSVへ接続・操作(MSDASQLプロバイダ編)
・ODBCでAccess・Excel・CSVへ接続・操作
2.DAOの文法について
DAOの全体の手順として、以下のようになります。図03
「DAOオブジェクト生成」から「データベース接続」までは同じですが、データのやり取りの部分(図03のOpenRecordsetメソッド・Executeメソッド部)で二つに分かれます。
① | テーブル名を指定して「全データ」を取得後、自分で(挿入・変更・削除も含めて)データを処理 | |
② | SQL文を指定。Select文では取得した「絞り込みデータ」を自分で処理し、それ以外(Insert、Update、Delete)ではデータベース側で処理が実行される |
「テーブル名を指定」する処理方法を例えるとすれば、Excelのワークシート上のデータを上から順に調べながら、対象となるデータを変更したり削除したりするイメージです。SQL文が苦手な方には取り付き易い手法かもしれません。
2ー1.DAOオブジェクト生成
DAOオブジェクトを生成するには、事前バインディングと実行時バインディングの2種類の方法があります。事前バインディングでは、図04のように「Microsoft Office XX.X Access database engine Object Library」にチェックをし有効にします。
図04
実行時バインディングの場合は、下記のようにCreateObjectでオブジェクトを生成します。
「Set DBEngineのオブジェクト名 = CreateObject("DAO.DBEngine.120")」
なお対象がAccessの場合、拡張子が「.mbd」の古いAccess(2007未満)の時は、以下のように設定してもOKのようです。 実行時バインディング:「Set DBEngineのオブジェクト名 = CreateObject("DAO.DBEngine.36")」 事前バインディング:「Microsoft DAO 3.6 Object Library」を有効化 但しOfficeが64ビット版の場合は、これらの設定をしようとするとエラーが発生します。一方32ビット版では正常に設定できますが、新しい拡張子「.accdb」のデータベースファイルを操作しようとすると、やはりエラーが発生してしまいます。また、データベースファイルを作成した時のバージョンでも可不可が以下のように異なります。
|
今回のサンプルコードでは、新しいAccessを対象とし、実行時バインディングでDAOオブジェクトを生成していきます。
2ー2.データベース接続
データベースに接続するには、下記のようにOpenDatabaseメソッドを使います。構文は以下のようになります。DBEngine.OpenDatabase(Name, Options, ReadOnly, Connect)
戻り値はデータベースオブジェクトになります。
パラメータは4つあり、それぞれの内容は以下のようになります。
パラメータ | 内容 | |
---|---|---|
Name | 必須 | DBファイル名 |
Options | 省略可 | True=排他モード,False(既定)=共有モード |
ReadOnly | 省略可 | True=読取専用,False(既定)=読取/書込 |
Connect | 省略可 | パスワード等の接続情報 Access以外へ接続する為の情報 |
必須パラメータ(Name)である「データベースファイル名」のみを指定した場合は、以下のような感じになります。
Set DBオブジェクト名 = DBEngineのオブジェクト名.OpenDatabase(データベースファイル名)
なお、ここで言う「データベースファイル」とは、対象のデータベースにより、ちょっとニュアンスが変わってきます。データベース毎にまとめたのが図07です。併行して「テーブル」の定義も載せています。
データベースファイル | テーブル | |
---|---|---|
Access | *.accdb, *.mdb | テーブル名 |
Excel | *.xlsx 等 | ワークシート名 |
CSV等 | CSVファイルがあるフォルダー | CSVファイル名 |
AccessやExcelのように、ファイル内に複数のテーブルを持てるタイプでは「ファイル名=データベースファイル」となりますが、CSVファイルのようにファイル=テーブルのようなものは「フォルダー名=データベースファイル」となります。
なおMicrosoftのサイトでは、Nameパラメータは「DBファイル名、又はODBCデータソース名(DSN)」と説明しています。 この説明だと「DAO → ODBC」というルートが可能となるのですが、DSNを指定してみても「見つかりませんでした」のようなエラーが発生してしまいます。ファイルDSNなども試してみましたが動作せず、今のところ設定の仕方が不明のままです。 |
第4パラメータの「Connect」を使用すると、ExcelやCSV等に接続することが可能となりますし、またデータの構造なども指定することができます。Connectに指定するキーワードを図08に整理してみました(他にも項目は存在するようです)。
項目 | 内容 |
---|---|
先頭部 | 無し=Access "Excel 12.0":Excel2007~ "Excel 8.0":~Excel2003 "Text":CSVやテキストファイル ""(空文字):DATABASEの指定でもOK(Access以外) |
HDR | YES(既定)=先頭行がタイトル NO=先頭行からデータ |
IMEX | 0=書込(データ型混在時はエラー:以下で説明) 1=読取専用(データ型混在時、文字列型で読込み:同) 2=読取/書込(データ型混在時はエラー:同) |
FMT | テキストファイルの区切り種類(カンマ区切りのみ) Delimited、又はCSVDelimited |
DATABASE | DBの場所(最後の「¥」有無はどちらでも可) 先頭でDBのPathを指定した時は不要 |
データベース名は通常「OpenDatabaseの第1パラメータ(Name)に設定」しますが、OpenDatabaseの第1パラメータ(Name)に「""(空文字)」を指定+接続文字列のDatabaseに「Pathを含めたデータベース名」を指定 することでも正常に動作します。但しデータベースがAccessの場合には通じません。
HDRは、データの先頭行が「タイトル行(Yes)」か「データ(No)」かの指定です。
IMEXは開くモードの指定です。他サイトでは、以下のように説明しています。
IMEX=0:書き込み(データ型が混在している時にはエラー発生)
IMEX=1:読み取り専用(データ型混在時は文字列型で読み込む)
IMEX=2:読み取り/書き込み(データ型が混在している時にはエラー発生)
この検証のため、第4パラメータが必須の「Excelシートデータベース」「CSVファイル」を対象にして、今回「コード例」内で条件を振って試してみた結果が図09です。
なお図09の、左端の「Excel」「CSV」は対象とするデータベースを、「Table」「SQL」はOpenRecordsetやExecuteメソッドへ指定する第1パラメータをテーブルにするかSQL文にするかを、「Read」「Write」は、処理が参照(Select)か変更(Insert,Update,Delte)かを表しています。
IMEX | ||||||
---|---|---|---|---|---|---|
0 | 1 | 2 | 省略 | |||
Excel | Table | Read | 〇/△ | 〇/〇 | 〇/△ | 〇/△ |
Write | 〇/〇 | ×/× | ×/× | 〇/〇 | ||
SQL | Read | 〇/× | 〇/〇 | 〇/× | 〇/× | |
Write | 〇/〇 | ×/× | ×/× | 〇/〇 | ||
CSV | Table | Read | 〇/〇 | 〇/〇 | 〇/〇 | 〇/〇 |
Write | 〇/〇 | 〇/〇 | 〇/〇 | 〇/〇 | ||
SQL | Read | 〇/× | 〇/× | 〇/× | 〇/× | |
Write | 〇/〇 | 〇/〇 | 〇/〇 | 〇/〇 |
△印:混在データ型は空白データとなる
SQL×WriteのみExecute。他はOpenRecordset。
図09
この結果を見る限り、他サイトが説明している状態とはちょっと異なるようです。特にIMEX=2はExcelを使うと、書き込みが出来ない結果となりました。
また「IMEX=1」は、他サイトでも言われている通り「データ型混在時には文字列型で読み込み」となります。但し、先頭から8行までの間に異なるデータ型が出てこないと「全て同一のデータ型」と認識されてしまい、9行目以降で異なったデータ型が出てくるとエラーが発生してしまう事には注意が必要です。
なお、この為に「TypeGuessRows(データ形式の推測を行う行数)」が存在しているはずなのですが、TypeGuessRowsに何を指定しても、値は「8(8行分でデータ型を推定)」のままです。つまりDAOでは「9行目以降にデータ型が混在しているデータベースはエラーが発生」することになります。
なおIMEX省略時(≒既定値)は、図09を見る限りでは「IMEX=0」のようです。
また「FMT」はテキストファイルのデータ間を何で区切るか(FMTはFormatの略)の指定です。元々は「CSVDelimited」がカンマ区切り、「TABDelimited」がTAB区切りだったようですが、「テキストファイルはカンマ区切りのみ」がデータベースとして操作可能です。「ACEではカンマのみだが、JETではTABも可能」との記述も見つけたのですが、うまくいきませんでした。
設定しなくても問題無いのですが、設定するのであれば「Delimited」か「CSVDelimited」を指定して下さい。
DATABASEには、データベースのパス名+ファイル名を指定でき、その際には先頭部は「""(空文字)」でも良いとの説明をしましたが、現在のところ明確なメリットが見いだせずにいます。
2ー3.データベースとのデータやり取り
DAOでデータベース側とデータのやりとりを行うには、OpenRecordsetメソッド及びExecuteメソッドを使用します。図03でも示したように、テーブル名を指定する方法ではOpenRecordsetメソッドのみを使用します。またSQL文を指定する方法では、Select文ではOpenRecordsetメソッドを使用し、それ以外(Insert,Update,Delete)ではExecuteメソッドを使用します。2ー3ー1.OpenRecordsetメソッド
データベースからデータを取得するにはOpenRecordsetメソッドを使用します。構文は以下のようになり、戻り値はレコードセットオブジェクトとなります。DBEngine.OpenRecordset(Name, Type, Options, LockEdit)
パラメータは図10の4つです。第1パラメータのNameのみ必須で、ここに「テーブル名①」または「SQL文②(Select文)」を文字列で指定します。
パラメータ | 内容 | |
---|---|---|
Name | 必須 | Table名① SQL文② |
Type | 省略可 | 開く形(図11参照) |
Options | 省略可 | 特性(図12参照) |
LockEdit | 省略可 | ロック方式(図13参照) |
テーブル名は、データベースがAccessの時は「テーブル名」、Excelの時は「ワークシート名(両端の角カッコは不要)」、CSV等のテキストファイルを使う時は「ファイル名(両端の角カッコは不要)」となります。
なお、アプリであるAccessでもVBAが使えますが、AccessのVBAでのOpenRecordsetメソッドと、ExcelのVBAでのOpenRecordsetメソッドはちょっと異なっているようです。色々調べたり試したりしても、どうしてもスッキリとしないのは、世の中のサイトの説明が皆Access寄りであって、私が実行しているのがExcelの環境であるためでは? と感じています。 以降の説明もAccess寄りになってしまうのですが、Excel VBAでの試行については「パラメータの組み合わせについて」を参考にして下さい。 |
第2パラメータのTypeは、レコードセットを開く形を図11の様に指定します。
定数 | 値 | 内容 |
---|---|---|
dbOpenTable | 1(既定①) | テーブルタイプで開く (テーブル全データをPCメモリー上に展開。ローカルデータのみが対象) |
dbOpenDynaset | 2(既定②) | ダイナセットタイプで開く (クエリを実行した結果を、更新可の状態で表示) |
dbOpenSnapshot | 4 | スナップショットタイプで開く (クエリを実行した結果のため更新不可) |
dbOpenForwardOnly | 8 | 前方スクロールタイプで開く (スクロール方向が前方向のみで更新不可) |
dbOpenDynamic | 16 | ダイナミックタイプで開く(ODBCDirectワークスペースのみで使用可) |
図11の「テーブルタイプ」「ダイナセットタイプ」「スナップショットタイプ」「前方スクロールタイプ」「ダイナミックタイプ」の違いを色々調べてはみました。しかし違いが良く分からない上に、図11内でカッコに記したような単純な動作では無いことも分かりました。図16・図18では、Nameパラメータ+Typeパラメータ+Optionパラメータの組み合わせ時にデータベースへの可能な実行内容をまとめましたので、参照下さい。
OpenRecordsetメソッドの第3パラメータ(Option)には、図12のような値が設定可能です。
定数 | 値 | 内容 |
---|---|---|
dbDenyWrite | 1 | 他ユーザーはレコードの修正・追加が不可 |
dbDenyRead | 2 | 他ユーザーはテーブルのデータの読み取り不可 |
dbReadOnly | 4 | 読み取り専用として開く |
dbAppendOnly | 8 | Insertは可。Select/Update/Deleteは不可 |
dbInconsistent | 16 | 矛盾を許す更新を可能にする |
dbConsistent | 32 | 他のレコードに影響を与えないフィールドにのみ更新 |
dbSQLPassThrough | 64 | ODBC データベースに SQL ステートメントを送信します |
dbFailOnError | 128 | エラーが発生した場合、更新をロールバック |
dbForwardOnly | 256 | 前方スクロールタイプのRecordsetを作成 |
dbSeeChanges | 512 | 編集中データを他ユーザーが変更しようとした場合、エラーが発生 |
dbRunAsync | 1024 | クエリを非同期で実行 |
dbExecDirect | 2048 | SQLPrepare ODBC 関数を最初に呼び出さずに、クエリを実行 |
第4パラメータのLockEditには、図13のような値が設定可能です。
定数 | 値 | 内容 |
---|---|---|
dbOptimisticValue | 1 | 列値に基づく共有的同時ロック(他ユーザーの変更を禁止・参照許可)(ODBCDirectワークスペースのみ) |
dbPessimistic | 2 | 排他的同時ロック(他ユーザーの参照・変更を禁止)LockEdits=True |
dbOptimistic | 3 | レコードIDに基づく共有的同時ロック(他ユーザーの変更を禁止)LockEdits=False |
dbOptimisticBatch | 5 | 共有的バッチ更新を可能 (ODBCDirectワークスペースのみ) |
2ー3ー2.Excecuteメソッド
データベースのデータに変更を加えるにはExcecuteメソッドを使用します。構文は以下のようになります。DBEngine.Excecute(Query, Options)
第1パラメータのQueryは、SQL文を文字列で指定します。
第2パラメータのOptions(省略可)は、図14の値を指定します。なおMicrosoftサイトの説明では、図12の「RecordsetOptionEnum定数」が使用可とも読み取ることが出来ます。試してみると確かに図14以外の定数値でも動作しますし、図14の中にも動かない定数もあります。この動作可不可については図18で紹介します。
定数 | 値 | 内容 |
---|---|---|
dbDenyWrite | 1 | 他ユーザーはレコードの修正・追加が不可 |
dbInconsistent | 16 | 矛盾を許す更新を可能にする |
dbConsistent | 32 | 他のレコードに影響を与えないフィールドにのみ更新 |
dbSQLPassThrough | 64 | ODBC データベースに SQL ステートメントを送信します |
dbFailOnError | 128 | エラーが発生した場合、更新をロールバック |
dbSeeChanges | 512 | 編集中データを他ユーザーが変更しようとした場合、エラーが発生 |
dbRunAsync | 1024 | クエリを非同期で実行 |
dbExecDirect | 2048 | SQLPrepare ODBC 関数を最初に呼び出さずに、クエリを実行 |
2ー3ー3.パラメータの組み合わせについて
2ー3ー3ー1.Accessとしての組み合わせ
OpenRecordsetメソッドの4つのパラメータと、Executeメソッドの2つのパラメータについて説明してきましたが、この全ての組み合わせが設定可能な訳ではありません。Microsoftを含む様々なサイトで調べてみると、TypeパラメータとOptionsパラメータの間には、図15のような制限がありそうです。〇が使用可と言われているものです。定数 | 値 | Typeパラメータ値 | |||
---|---|---|---|---|---|
1 | 2 | 4 | 8 | ||
dbDenyWrite | 1 | 〇 | 〇 | 〇 | 〇 |
dbDenyRead | 2 | 〇 | |||
dbReadOnly | 4 | 〇 | 〇 | 〇 | 〇 |
dbAppendOnly | 8 | 〇 | |||
dbInconsistent | 16 | 〇 | 〇 | ||
dbConsistent | 32 | 〇 | 〇 | ||
dbSQLPassThrough | 64 | 〇 | |||
dbFailOnError | 128 | (〇) | |||
dbForwardOnly | 256 | 〇 | |||
dbSeeChanges | 512 | 〇 | |||
dbRunAsync | 1024 | ? | ? | ? | ? |
dbExecDirect | 2048 | ? | ? | ? | ? |
但し、この対照表はAccessのVBAでの制限のようで、Excelから動かそうとした場合は違う組み合わせになるようです。なおTypeの定数dbOpenDynamic(値=16)については情報が全く無いため省略しました。
2ー3ー3ー2.Excel VBAとしてのTypeとOptionsパラメータの組み合わせ
今回ExcelのVBAで、OpenRecordsetの第2パラメータ(Type)と第3パラメータ(Options)実際に試してみると、図15とは異なる結果となりました。まず第1パラメータにテーブルを指定した場合が図16です。定数 | 値 | Typeパラメータ値 | ||||
---|---|---|---|---|---|---|
1 | 2 | 4 | 8 | 16 | ||
dbDenyWrite | 1 | SIUD | SIUD | S・・・ | S・・・ | ・・・・ |
dbDenyRead | 2 | SIUD | SIUD | S・・・ | S・・・ | ・・・・ |
dbReadOnly | 4 | S・・・ | S・・・ | S・・・ | S・・・ | ・・・・ |
dbAppendOnly | 8 | SIUD | ・I・・ | S・・・ | S・・・ | ・・・・ |
dbInconsistent | 16 | SIUD | SIUD | S・・・ | S・・・ | ・・・・ |
dbConsistent | 32 | SIUD | SIUD | S・・・ | S・・・ | ・・・・ |
dbSQLPassThrough | 64 | SIUD | ・・・・ | ・・・・ | ・・・・ | ・・・・ |
dbFailOnError | 128 | SIUD | SIUD | S・・・ | S・・・ | ・・・・ |
dbForwardOnly | 256 | SIUD | S・・・ | S・・・ | S・・・ | ・・・・ |
dbSeeChanges | 512 | SIUD | SIUD | S・・・ | S・・・ | ・・・・ |
dbRunAsync | 1024 | ・・・・ | ・・・・ | ・・・・ | ・・・・ | ・・・・ |
dbExecDirect | 2048 | ・・・・ | ・・・・ | ・・・・ | ・・・・ | ・・・・ |
図16
表中の例えば「SIUD」の組み合わせでは、Select処理・Insert処理・Update処理・Delete処理が(Accessでは)実行可能を示しており、また「・I・・」はInsert処理のみ実行可能を示します。「・・・・」は、どの処理も不可(≒レコードセットが作成できない)を表します。
なお図16はAccessのデータベースファイルで試したものであり、ExcelのワークシートではDeleteは実行できず、CSVファイルではUpdateとDeleteが実行できませんのでご注意下さい。
図15と図16を比べると結構違います。とても同じ表とは思えません。そこでテーブルを指定し、TypeパラメータとOptionsパラメータを振った時のレコードセットのプロパティ(違いが出るものを抽出)を調べてみた結果が図17です。
なお対象のデータベースは、Accessデータ(4レコード在中)を使用しています。
図17
Type=16とOptions=1024,2048はレコードセットが戻されないため省略していますが、赤字にした部分が周辺のプロパティ値と異なる事を強調しているつもりです。図16での異なる部分と、図17の異なる部分はほぼ一致している様に見えます。
また、第1パラメータにSQL文を指定する場合は、図18のような結果です。Select文はOpenRecordsetメソッドを使いますし、それ以外はExecuteメソッドを使いますが、Optionsパラメータは同じ種類ですので同じ表としてまとめました。
Options定数 | 値 | OpenRecordset | Execute | ||||||
---|---|---|---|---|---|---|---|---|---|
Select文 | Ins 文 | Up 文 | Del 文 | ||||||
Typeパラメータ値 | |||||||||
1 | 2 | 4 | 8 | 16 | |||||
db | 1 | × | 〇 | 〇 | 〇 | × | 〇 | 〇 | 〇 |
db | 2 | × | 〇 | 〇 | 〇 | × | 〇 | 〇 | 〇 |
db | 4 | × | 〇 | 〇 | 〇 | × | 〇 | 〇 | 〇 |
db | 8 | × | × | 〇 | 〇 | × | 〇 | 〇 | 〇 |
db | 16 | × | 〇 | 〇 | 〇 | × | 〇 | 〇 | 〇 |
db | 32 | × | 〇 | 〇 | 〇 | × | 〇 | 〇 | 〇 |
db | 64 | 〇 | 〇 | 〇 | 〇 | × | 〇 | 〇 | 〇 |
db | 128 | × | 〇 | 〇 | 〇 | × | 〇 | 〇 | 〇 |
db | 256 | × | 〇 | 〇 | 〇 | × | 〇 | 〇 | 〇 |
db | 512 | × | 〇 | 〇 | 〇 | × | 〇 | 〇 | 〇 |
db | 1024 | × | × | × | × | × | × | × | × |
db | 2048 | × | × | × | × | × | × | × | × |
SQL文を指定し、TypeパラメータとOptionsパラメータを振った時のレコードセットのプロパティの結果が図19です。
こちらも、図18のOpenRecordset部での異なる部分と図19の異なる部分は、ほぼ一致している様に見えます。
図19
以上の結果を見てみると、一般的に言われている図15のような適合可否は、Access上でOpenRecordsetやExecuteメソッドを使用する場合のものなのかもしれません(Accessを保有していないので未確認です)。ExcelのVBAで使用する限りでは、図16や図18を参考にしてパラメータを決めて頂ければ良いかと思います。
2ー3ー3ー3.Excel VBAとしてのLockEditパラメータの組み合わせ
第4パラメータのLockEditは、Accessでは「True(図13のdbPessimistic相当)」が既定値のようです。第4パラメータを指定せずにOpenRecordsetメソッドを実行した際にも、図17・図19の表のLockEditsプロパティ値のように「True 又は 使用不可」となります。LockEditパラメータは、このTrueとなっている組み合わせに対して「True=dbPessimistic
3.コード例
今回のコード例では、「Access」「Excel」「CSV(カンマ区切りテキスト)」のデータを扱います。そのデータは、以下のような形の「4列×4行」としました。St、Nu、Dt、BLが列名になります。Excel、CSVも先頭行は列名行としています。St | Nu | Dt | BL |
---|---|---|---|
"abc" | 123 | 2023/1/2 | True |
"def" | 123 | 2023/2/2 | False |
"abc" | 456 | 2023/3/2 | False |
"def" | 456 | 2023/4/2 | True |
Excelデータは手書きでワークシート上に並べ、CSVデータはメモ帳などで手書き入力しました。なおAccessデータはAccessアプリで作成するのでは無く、「Accessデータの作成について」で紹介するように「Excel VBAからデータを生成」させています。
ファイル名等は、以下の通りになります。
データベース | テーブル | |
---|---|---|
Access | its-041.accdb | "testtable" |
Excel | its-041.xlsm | "DB" |
CSV | (ExcelのPath) | its-041.csv |
3ー1.テーブル名を指定する場合
3ー1ー1.Accessデータの場合
OpenRecordsetメソッドの第1パラメータに「Accessのテーブル名」を指定し、全データをレコードセットとして取り出した後に自分で処理を行うコードの例が図22になります。- '========== ⇩(1) Accessデータでテーブル名を指定する場合 ============
- Sub DAO_Access01()
- Dim DBEngine As Object '←DBエンジン
- Dim DBname As String '←データベース名
- Dim DB As Object '←データベース
- Dim SQL As String '←実行するテーブル名
- Dim rs As Object '←レコードセット
- Dim i As Integer '←書き込み行位置
- Set DBEngine = CreateObject("DAO.DBEngine.120")
- DBname = ThisWorkbook.Path & "¥" & "its-041.accdb"
- Set DB = DBEngine.OpenDatabase(DBname)
- SQL = "testtable"
- Set rs = DB.OpenRecordset(SQL)
- Sheet1.Range("B2")
.CurrentRegion .ClearContents - With Sheet1.Range("B2")
- Do While Not rs.EOF
- .Offset(i, 0) = rs.Fields(0).Value
- .Offset(i, 1) = rs.Fields(1).Value
- .Offset(i, 2) = rs.Fields(2).Value
- .Offset(i, 3) = rs.Fields(3).Value
- rs.movenext
- i = i + 1
- Loop
- End With
- rs.Close
- DB.Close
- Set rs = Nothing
- Set DB = Nothing
- Set DBEngine = Nothing
- End Sub
09行目「Set DBEngine = CreateObject("DAO.DBEngine.120")」では、DAOのDBエンジンオブジェクトを生成します。
11行目「DBname = ThisWorkbook.Path & "¥" & "its-041.accdb"」では、接続するデータベース(Accessの場合は、Accessファイル名)を変数DBnameに代入しています。
12行目「Set DB = DBEngine.OpenDatabase(DBname)」で、データベースに接続します。
14行目「SQL = "testtable"」は、取得するテーブル名を変数SQLとしています。
15行目「Set rs = DB.OpenRecordset(SQL)」では、14行目のテーブルの全データを取得し、レコードセット「rs」としています。レコードセットには、カーソルの位置にあるレコードのデータが見えていますので、列(フィールド)を指定することで、その値が取り出せます。
17~29行目では、データをExcelのシートに書き出しています。SQL文で言えばSelectに相当します。
まず17行目「Sheet1.Range("B2")
19行目「With Sheet1.Range("B2")」で、シートへの書き込みの基準セルを決めます。
20~28行目のDo~Loopで、データを最終行まで処理していきます。カーソルを移動させるのは26行目「rs.movenext」です。
20行目「Do While Not rs.EOF」で、レコードセットの最終行が来るまで繰り返します。
21行目「.Offset(i, 0) = rs.Fields(0).Value」では、1列目(rs.Fields(0))の値を19行目で指定した基準セル列のi個下のセル(.Offset(i, 0))に書き込みます(列番号はゼロ始まり)。
22~24行目も同様に、2~4列目の値を横に並べて書き込んでいきます。なお、今回は図20のように各列を色々なデータ型にしています。もしセルに貼り付けた時に表示がおかしくなる(例えば、日付が数値として表示される)場合には、CDateやFormatなどを使って揃えて下さい。
なお列の値は「rs.Fields(0).Value」という書き方の他にも、列名を使って「rs.Fields("St").Value」「rs!St.Value」や「rs.Fields!St.Value」としてもOKです。
また「For j = 0 To rs.filelds.Count - 1」のように、カウンタ変数を列数分だけ回すようにすれば、21~24行目を1行で表すことも可能です。但し行毎に表示形式を変更するような細工は難しくなります。
26行目「rs.movenext」では、レコードセットのカーソルを次の行に移動させています。レコードセットのAbsolutePositionプロパティが1つ増える形になります。
なお、カーソルを移動するにはMoveNextを含めて、以下の5種があります。
MoveFirst:先頭レコードに移動
MoveLast:最終レコードに移動
MoveNext:次のレコードに移動
MovePrevious: 1つ前のレコードに移動
Move [行位置]:指定した行位置に移動
27行目「i = i + 1」は、21~24行目の「書き込むセルの行位置」を下に移動させています。
レコードセットのカーソルが最終行(rs.EOF = True)まで達したら、31行目「rs.Close」でレコードセットを閉じ、32行目「DB.Close」でデータベースを閉じます。
図22は「Select相当」のコードでしたが、レコードセットに対して「データを挿入(Insert相当)」するには図23のようなコードにします。図22の17~29行目を下記で置き換える形です。
- '========== ⇩(2) データの挿入 ============
- rs.AddNew
- rs.Fields(0) = "PQR"
- rs.Fields(1) = 897
- rs.Fields(2) = #11/20/2000#
- rs.Fields(3) = True
- rs.Update
41行目「rs.AddNew」で、レコードセットのEditModeプロパティ値が「0 → 2」に変更され、新たな行を準備します。Addしたからと言って、この段階ではテーブルの行数はまだ増えていません。
42行目「rs.Fields(0) = "PQR"」では、1列目にデータを入れます。43~45行目も2~4列目にデータを入れます。
最後に46行目「rs.Update」で、レコードセットを更新させることで、レコードセットに1行追加されます。
レコードセットに対して「データを変更(Update相当)」するには図24のようなコードにします。図22の17~29行目を下記で置き換える形です。
- '========== ⇩(3) データの変更 ============
- Do While Not rs.EOF
- If CDate(rs.Fields(2).Value) = #11/20/2000# Then
- rs.Edit
- rs.Fields(0) = "XYZ"
- rs.Update
- End If
- rs.movenext
- Loop
51行目「Do While Not rs.EOF」で、レコードセットの最終行が来るまで繰り返します。
52行目「If CDate(rs.Fields(2).Value) = #11/20/2000# Then」で、変更するレコードを探します。SQL文でのWhere句に相当します。レコードが見つかったら、53~55行目を実行します。
53行目「rs.Edit」では、レコードセットのEditModeプロパティ値が「0 → 1」に変更され、編集モードになります。
54行目「rs.Fields(0) = "XYZ"」で、目的の列の値を変更し、55行目「rs.Update」でレコードセットを更新します。
55行目の更新処理は、変更した各行毎(movenextをする前)に行う必要があります。
58行目「rs.movenext」ではカーソル行を次行に移動させ、52行目の検索を続けていきます。
レコードセットに対して「データを削除(Delete相当)」するには図25のようなコードにします。図22の17~29行目を下記で置き換える形です。
- '========== ⇩(4) データの削除 ============
- Do While Not rs.EOF
- If CDate(rs.Fields(2).Value) = #11/20/2000# Then
- rs.Delete
- End If
- rs.movenext
- Loop
71行目「Do While Not rs.EOF」で、レコードセットの最終行が来るまで繰り返します。
72行目「If CDate(rs.Fields(2).Value) = #11/20/2000# Then」で、削除するレコードを探します。SQL文でのWhere句に相当します。レコードが見つかったら、73行目を実行します。
73行目「rs.Delete」では、カーソルのあるレコードを削除します。Deleteの場合は、EditModeを経ることなく直接削除してしまいますので、挿入や変更の時のような「rs.Update」は不要です。
76行目「rs.movenext」ではカーソル行を次行に移動させ、72行目の検索を続けていきます。
3ー1ー2.Excelデータの場合
3ー1ー2ー1.Select相当
データがExcelのワークシート上(ここでは、Sheets("DB")上 )にある場合は、OpenRecordsetメソッドの第1パラメータに「Excelのシート名」を指定することで、全データのレコードセットを取り出せます。- '========== ⇩(5) Excelデータでテーブル名を指定する場合 ============
- Sub DAO_Excel01()
- Dim DBEngine As Object '←DBエンジン
- Dim DBname As String '←データベース名
- Dim DB As Object '←データベース
- Dim SQL As String '←実行するテーブル名
- Dim rs As Object '←レコードセット
- Dim i As Integer '←書き込み行位置
- Set DBEngine = CreateObject("DAO.DBEngine.120")
- DBname = ThisWorkbook.Path & "¥" & ThisWorkbook.Name
- Set DB = DBEngine.OpenDatabase(DBname, False, False, "Excel 12.0;HDR=Yes;IMEX=1;")
- SQL = "DB$" '←データベースのシート名
- Set rs = DB.OpenRecordset(SQL)
- Sheet1.Range("B2").CurrentRegion
.ClearContents - With Sheet1.Range("B2")
- Do While Not rs.EOF
- .Offset(i, 0) = rs.Fields(0).Value
- .Offset(i, 1) = rs.Fields(1).Value
- .Offset(i, 2) = rs.Fields(2).Value
- .Offset(i, 3) = rs.Fields(3).Value
- rs.movenext
- i = i + 1
- Loop
- End With
- rs.Close
- DB.Close
- Set rs = Nothing
- Set DB = Nothing
- Set DBEngine = Nothing
- End Sub
89行目「Set DBEngine = CreateObject("DAO.DBEngine.120")」では、DAOのDBエンジンオブジェクトを生成します。
91行目「DBname = ThisWorkbook.Path & "¥" & ThisWorkbook.Name」では、接続するデータベース(Excelの場合は、Excelファイル名)を変数DBnameに代入しています。今回は、マクロのあるブック上にデータベースがありますので、ThisWorkbookとなります。
92行目「Set DB = DBEngine.OpenDatabase(DBname, False, False, "Excel 12.0;HDR=Yes;IMEX=1;")」で、データベースに接続しています。
Accessの時とは異なり、OpenDatabaseメソッドに4つのパラメータ(図06参照)を指定する必要があります。
まず第1パラメータNameは、Accessの時と同じく「データベース名」です。
第2パラメータOptionsと第3パラメータReadOnlyは既定値(共有モード × 読取/書込)で良い場合でも値(今回は両方ともFalse)を指定しないと、エラーになるようです(Accessの時は、図22の12行目のように両方とも省略しても問題無い)。
第4パラメータConnectへは「Excelへの接続情報」として「"Excel 12.0;HDR=Yes;IMEX=1;"」を指定します。書き方としては項目を「;(セミコロン)」で区切り、全体としては文字列とします。
先頭の「Excel 12.0」は、接続するデータベースの種類としてExcelを指定します。且つバージョンがExcel2007以降ですので「12.0」となります。一方Excel2003以前の場合は「8.0」です。
次の「HDR=Yes」は先頭行がタイトル行か否かの設定です。Yesが既定値ですが、明示的に指定しています。
最後の「IMEX=1」は読取/書込の設定で、ここでは「読取(値=1)」を指定しています。なお、図09でも説明したように、同じ列内にデータ型が混在していなければIMEX値は何でもOKですが、混在している場合に「0=書込」又は「2=読取/書込」を指定してしまうと「データ型の異なる値はNull」となりますので注意が必要です。
但し「1=読取」に設定したとしても、9行目以降に異なるデータ型が存在する場合は、同様にNullとなります。これは「データ型が揃っているか否かを調べる行数が8行」であるためです。 そのために「TypeGuessRows=」という設定が存在するはずなのですが、例え「TypeGuessRows=20;」等の設定を追加しても機能してくれず、「8行分しか調べない」のです。ですので、データ型は混在させない事が重要です。 |
この他にも「Database=」という設定も出来ます。ここにデータベースの場所+ファイル名を指定し、
「Set DB = DBEngine.OpenDatabase("", False, False, "Excel 12.0;HDR=Yes;IMEX=1;Database=" & DBname)」
とすることでもデータベースに接続できます。この場合、先頭のデータベース名の部分は空文字でOKです。
しかし、この書き方だと文字数が増えるだけなのでメリットはありません。恐らく「Database=」の設定を活かせる場面が別に存在するのだと思います。
94行目「SQL = "DB$"」は、テーブル相当(ExcelシートDBの場合は、シート名+「$」印)を変数SQLに代入します。「Set DB = DBEngine.OpenDatabase("", False, False, "Excel 12.0;HDR=Yes;IMEX=1;Database=" & DBname)」
とすることでもデータベースに接続できます。この場合、先頭のデータベース名の部分は空文字でOKです。
しかし、この書き方だと文字数が増えるだけなのでメリットはありません。恐らく「Database=」の設定を活かせる場面が別に存在するのだと思います。
95行目「Set rs = DB.OpenRecordset(SQL)」で、レコードセットを取得します。
以下は図22の時と全く同じで、97行目「Sheet1.Range("B2").CurrentRegion
1レコード文の処理が完了したら、106行目「rs.movenext」でカーソルを移動させ、書き込む行位置も107行目「i = i + 1」で下に移動させます。
3ー1ー2ー2.Insert、Update相当
図26はレコードセットを取得後、シート上にデータを書き出すというSQLで言うSelect操作でした。同じ手法でInsert、Updateも可能ですが、一箇所注意しなければいけないところがあります。Insert相当のコードを図27に示します。- '========== ⇩(6) Excelデータの更新(挿入) ============
- Sub DAO_Excel02()
- Dim DBEngine As Object '←DBエンジン
- Dim DBname As String '←データベース名
- Dim DB As Object '←データベース
- Dim SQL As String '←実行するテーブル名
- Dim rs As Object '←レコードセット
- Dim i As Integer '←書き込み行位置
- Set DBEngine = CreateObject("DAO.DBEngine.120")
- DBname = ThisWorkbook.Path & "¥" & ThisWorkbook.Name
- Set DB = DBEngine.OpenDatabase(DBname, False, False, "Excel 12.0;HDR=Yes;IMEX=0;")
- SQL = "DB$"
- Set rs = DB.OpenRecordset(SQL)
- rs.AddNew
- rs.Fields(0) = "PQR"
- rs.Fields(1) = 897
- rs.Fields(2) = #11/20/2000#
- rs.Fields(3) = True
- rs.Update
- rs.Close
- DB.Close
- Set rs = Nothing
- Set DB = Nothing
- Set DBEngine = Nothing
- End Sub
注意が必要なのは、132行目「Set DB = DBEngine.OpenDatabase(DBname, False, False, "Excel 12.0;HDR=Yes;IMEX=0;")」の「IMEX=0」の部分です。
Select相当の図26では、データ型混在時も考慮して「IMEX=1」としました。しかしデータに変更を加えるInsert相当やUpdate相当(図09の、Excel × Table × Write 行)では、「IMEX=1」又は「IMEX=2」を指定するとエラー(番号=3027:「データベースまたはオブジェクトは読み取り専用なので更新できません」)が発生してしまいます。
その他は同じで、137~142行目でデータ行の追加を行っています。内容はAccess時の図23と全く同じです。
データ変更(Update相当)の場合は、図27の137~142行目を図28のように置き換えます。内容はAccess時の図24と全く同じです。
- '========== ⇩(7) Excelデータの変更 ============
- Do While Not rs.EOF
- If CDate(rs.Fields(2).Value) = #11/20/2000# Then
- rs.Edit
- rs.Fields(0) = "XYZ"
- rs.Update
- End If
- rs.movenext
- Loop
なお、Excelシートをデータベースにする際は、「Select(参照)」「Insert(挿入)」「Update(変更)」操作のみが可能で、Delete(削除)操作を行おうとすると「このISAMではリンクテーブル内のデータを削除することはできません」と言う実行時エラーが発生します。
3ー1ー3.CSVデータの場合
データがCSV等のテキストファイル(ここでは、its-041.csv )である場合は、OpenRecordsetメソッドの第1パラメータに「ファイル名」を指定することで、全データのレコードセットを取り出せます。- '========== ⇩(8) CSVデータでファイル名を指定する場合 ============
- Sub DAO_CSV01()
- Dim DBEngine As Object '←DBエンジン
- Dim DBpath As String '←データベース名
- Dim DB As Object '←データベース
- Dim SQL As String '←実行するテーブル名
- Dim rs As Object '←レコードセット
- Dim i As Integer '←書き込み行位置
- Set DBEngine = CreateObject("DAO.DBEngine.120")
- DBpath = ThisWorkbook.Path
- Set DB = DBEngine.OpenDatabase(DBpath, False, False, "Text;HDR=Yes;")
- SQL = "its-041.csv"
- Set rs = DB.OpenRecordset(SQL)
- Sheet1.Range("B2").CurrentRegion
.ClearContents - With Sheet1.Range("B2")
- Do While Not rs.EOF
- .Offset(i, 0) = rs.Fields(0).Value
- .Offset(i, 1) = rs.Fields(1).Value
- .Offset(i, 2) = rs.Fields(2).Value
- .Offset(i, 3) = rs.Fields(3).Value
- rs.movenext
- i = i + 1
- Loop
- End With
- rs.Close
- DB.Close
- Set rs = Nothing
- Set DB = Nothing
- Set DBEngine = Nothing
- End Sub
189行目「Set DBEngine = CreateObject("DAO.DBEngine.120")」では、DAOのDBエンジンオブジェクトを生成します。
191行目「DBpath = ThisWorkbook.Path」では、接続するデータベース(テキストファイルの場合は、ファイルのおいてある場所)を変数DBpathに代入します。今回はマクロのあるブックと同じ場所にCSVファイルがあるので、ThisWorkbook
192行目「Set DB = DBEngine.OpenDatabase(DBpath, False, False, "Text;HDR=Yes;")」で、テキストファイルの存在する場所に接続しています。
Excelの時と同様に、OpenDatabaseメソッドに4つのパラメータ(図06参照)を指定する必要があります。
第1パラメータNameには、テキストファイルの存在する場所を指定します。
第2パラメータOptionsと第3パラメータReadOnlyは、既定値(共有モード × 読取/書込)で良い場合でも値(今回は両方ともFalse)を指定しないと、エラーになります。
第4パラメータConnectへは「テキストファイルへの接続」、及びテーブルの「先頭がタイトル行」である事から「"Text;HDR=Yes;"」を指定します。
なお、ここではIMEXは指定していませんが、図09でも分かる通り「CSV × Table」は、どれを指定しても〇のため指定していません。
194行目「SQL = "its-041.csv"」は、テキストファイル名を指定します。これがデータベースのテーブル相当になります。
195行目「Set rs = DB.OpenRecordset(SQL)」で、レコードセットを取得します。
以下は図22と全く同じで、199行目「With Sheet1.Range("B2")」でシートへの書き込みの基準セルを決め、200行目「Do While Not rs.EOF」でデータを最終行まで処理し、201行目「.Offset(i, 0) = rs.Fields(0).Value」で、シート上に1列目の値を書き込みます。202~204行目も2~4列目の値を書き込んでいます。
1レコード文の処理が完了したら、206行目「rs.movenext」でカーソルを移動させ、書き込む行位置も207行目「i = i + 1」で下に移動させます。
図29はレコードセット取得後、Excelシート上にデータを書き出すSQLのSelect相当の処理でした。同じ手法で図29の199~209行目を図30で置き換えることで、Insert操作(レコード追加)が可能です。内容は図23と全く同じです。
- '========== ⇩(9) CSVデータの追加 ============
- rs.AddNew
- rs.Fields(0) = "PQR"
- rs.Fields(1) = 897
- rs.Fields(2) = #11/20/2000#
- rs.Fields(3) = True
- rs.Update
CSVファイルに対して図30のコードでデータ挿入をすると、CSVファイル上は図31の左図のような形に書き加えられます。また、そのCSVデータを読み取ってワークシート上に書き出したのが図31の右側です。
図31
良く見てみると、まず3列目の日付型は、224行目「rs.Fields(2) = #11/20/2000#」では日付部分のみを指定しているのに、CSVファイル側は「2000/11/20 0:00:00」と時刻も共に書き込まれます。
また225行目「rs.Fields(3) = True」のBoolean型は、CSVファイル側には「-1」が「文字列」として書き込まれています。
日付型については、時刻部分が小数点以下に設定されているため、整数部だけでも午前零時と表示されても変ではありませんし、Boolean型については「True」をExcel上で数値で表すと「-1」になる為です(Falseはゼロ)。
但しマイナスが付いているとは言え、2列目の数値「897」には「ダブルクォーテーションが付いていない」のに対し、4列目の-1には「ダブルクォーテーションが付いている」のは不思議です。
そこで調べてみると、221行目のAddNewメソッドの実行時に作られる新しいレコード枠のデータ型は、その列の既存の値のデータ型で決まるようです。例えば4列目はTrueやFalseとBoolean型っぽく表示されていますが「TrueやFalseという文字列」です。つまり4列目は全て文字列なので「新たなレコード枠の4番目も文字列」となり、「-1」は文字列としてCSVファイルに追記されるようです。
なお、列内に複数のデータ型が混在している時には、最も多いデータ型になるようです。
このデータ型については「Microsoftのサイト」に載っています。このサイトでは、Typeプロパティは「取得/設定が可能」と書かれていますが、例えばBoolean型に設定しようとしてもエラーとなってしまいます。ですので、CSVファイルではデータ型を混在させないようにすることと、読み込み時にデータ型が混在している事も考慮して、CBoolなどの関数を使ってデータ型を揃える等の工夫は必要そうです。
なお、Excelのセル上に「"-1"」を書き出せば、Excel側で数値と判断して「-1」と修正してくれます(図31の右側)。また「rs.Fields(3) = False」のようにFalseを挿入した際には、CSVファイルには「0」が文字列として書き込まれ、ワークシートに出力した際は「数値の0」となります。
上記のように、テキストファイルをデータベースにする際には「型変換」等の処理が必要そうですが、対策の1つとして「日付とBoolean型を文字列」にしてデータ挿入してみたのが図32です。
- '========== ⇩(10) CSVデータの追加 ============
- rs.AddNew
- rs.Fields(0) = "PQR"
- rs.Fields(1) = 897
- rs.Fields(2) = "2000/11/20"
- rs.Fields(3) = "True"
- rs.Update
その結果が図33で、左側がCSVデータ、右側がシートに書き出したものです。
図33
日付型は変化無いものの、Boolean型を文字列にした場合は、CSVファイルには両端をダブルクォーテーションで囲まれた文字列「"True"(又は "False")」として記録されます。見掛け上は、1~4行目の(手書きで作った)TRUE(又は FALSE)とは別のデータ型に見えますが、ダブルクォーテーションが無いだけで「どちらも文字列扱い」です。
ですので、ワークシートに出力した際にはExcelの機能で「Booleanの文字列 → Boolean型」に勝手に変換されるため、Boolean型として扱う事ができますが、データを配列等に入れて使う場合はダブルクォーテーション有り無しに関わらず「CBoolなどで変換」する必要が出てきます。
また、VBAではTrueは「-1」ですが、C言語やJava等ではTrueは「1」のようです。CSVデータを他のプログラム言語からも使用できるようにするため、文字列のTrue・Falseとして記録する方が良いように思います。
なお、テキストファイルのInsert操作時は、Excelの時(図27)のように「IMEX=0」としなくても大丈夫です。これは図09の「CSV × Table × Write 行」が全て〇であることからも分かります。
また、テキストファイルでは「Select」操作と「Insert」操作のみが可能で、Update・Delete操作を行おうとすると「このISAMではリンクテーブル内のデータを更新/削除することはできません」と言う実行時エラーが発生します。
3ー2.SQL文を指定する場合
3ー2ー1.Accessデータの場合
3ー2ー1ー1.Select文
実行するSQL文が「Select」の場合は、OpenRecordsetメソッドにSQL文を指定し、絞り込んだレコードセットオブジェクトを取得した後、データ処理をします。- '========== ⇩(11) AccessデータをSelect文で取得 ============
- Sub DAO_Access02()
- Dim DBEngine As Object '←DBエンジン
- Dim DBname As String '←データベース名
- Dim DB As Object '←データベース
- Dim SQL As String '←実行するSQL文
- Dim rs As Object '←レコードセット
- Dim i As Integer '←書き込み行位置
- Set DBEngine = CreateObject("DAO.DBEngine.120")
- DBname = ThisWorkbook.Path & "¥" & "its-041.accdb"
- Set DB = DBEngine.OpenDatabase(DBname)
- SQL = "select * from TestTable"
- Set rs = DB.OpenRecordset(SQL)
- Sheet1.Range("B2").CurrentRegion
.ClearContents - With Sheet1.Range("B2")
- Do While Not rs.EOF
- .Offset(i, 0) = rs.Fields(0).Value
- .Offset(i, 1) = rs.Fields(1).Value
- .Offset(i, 2) = rs.Fields(2).Value
- .Offset(i, 3) = rs.Fields(3).Value
- rs.movenext
- i = i + 1
- Loop
- End With
- rs.Close
- DB.Close
- Set rs = Nothing
- Set DB = Nothing
- Set DBEngine = Nothing
- End Sub
249行目「Set DBEngine = CreateObject("DAO.DBEngine.120")」で、DAOのDBエンジンオブジェクトを生成します。
251行目「DBname = ThisWorkbook.Path & "¥" & "its-041.accdb"」で、接続するデータベース(Accessの場合は、Accessファイル名)を変数DBnameに代入します。
252行目「Set DB = DBEngine.OpenDatabase(DBname)」で、データベースに接続します。
254行目「SQL = "select * from TestTable"」は、実行するSQL文(Select文)を変数SQLとしています。
255行目「Set rs = DB.OpenRecordset(SQL)」は254行目のSelect文を実行し、絞り込んだデータをレコードセットとして取得します。
その後の処理はOpenRecordsetにテーブル名を指定した時と全く同じです。
257行目「Sheet1.Range("B2").CurrentRegion
1レコード文の処理が完了したら、266行目「rs.movenext」でカーソルを移動させ、書き込む行位置も267行目「i = i + 1」で下に移動させます。
3ー2ー1-2.Insert・Update・Delete文の場合
実行するSQL文が「Insert」「Update」「Delete」文の場合は、Executeメソッドを使用します。図35はデータを追加(Insert)するコードです。- '========== ⇩(12) AccessデータにInsert文で追加 ============
- Sub DAO_Access03()
- Dim DBEngine As Object '←DBエンジン
- Dim DBname As String '←データベース名
- Dim DB As Object '←データベース
- Dim SQL As String '←実行するSQL文
- Set DBEngine = CreateObject("DAO.DBEngine.120")
- DBname = ThisWorkbook.Path & "¥" & "its-041.accdb"
- Set DB = DBEngine.OpenDatabase(DBname)
- SQL = "insert into TestTable (St,Nu,Dt,BL) values('PQR',897,#2000/11/20#,True)"
' SQL = "update TestTable set St='XYZ' where DT=#2000/11/20#"' SQL = "Delete from TestTable where Dt = #2000/11/20#"- DB.Execute (SQL)
- DB.Close
- Set DB = Nothing
- Set DBEngine = Nothing
- End Sub
297行目「Set DBEngine = CreateObject("DAO.DBEngine.120")」で、DAOのDBエンジンオブジェクトを生成します。
299行目「DBname = ThisWorkbook.Path & "¥" & "its-041.accdb"」で、接続するデータベース(Accessの場合は、Accessファイル名)を変数DBnameに代入します。
300行目「Set DB = DBEngine.OpenDatabase(DBname)」で、データベースに接続します。
302行目「SQL = "insert into TestTable (St,Nu,Dt,BL) values('PQR',897,#2000/11/20#,True)"」のSQL文は、新たなレコードを追加するものです。
そのSQL文を306行目「DB.Execute (SQL)」で実行することで、Accessへデータを追加します。
ここではレコードセットを開いていないので、閉じるのはデータベースのみで、308行目「DB.Close」のみを行います。
ここまでデータ挿入(Insert)について説明しましたが、見え消しにしてあるSQL文の
303行目「SQL = "update TestTable set St='XYZ' where DT=#2000/11/20#"」
304行目「SQL = "Delete from TestTable where Dt = #2000/11/20#"」
のようなUpdate文、Delete文を使えば、レコード内容の変更・レコード削除が出来ます。
なおSQL文の構文などについては、他サイトや本サイト「データを操作するSQL文」を参照下さい。
3ー2ー2.Excelデータの場合
3ー2ー2ー1.Select文
実行するSQL文が「Select」の場合は、OpenRecordsetメソッドにSQL文を指定し、絞り込んだレコードセットオブジェクトを取得した後、データ処理をします。- '========== ⇩(13) ExcelデータをSelect文で取得 ============
- Sub DAO_Excel03()
- Dim DBEngine As Object '←DBエンジン
- Dim DBname As String '←データベース名
- Dim DB As Object '←データベース
- Dim SQL As String '←実行するSQL文
- Dim rs As Object '←レコードセット
- Dim i As Integer '←書き込み行位置
- Set DBEngine = CreateObject("DAO.DBEngine.120")
- DBname = ThisWorkbook.Path & "¥" & ThisWorkbook.Name
- Set DB = DBEngine.OpenDatabase(DBname, False, False, "Excel 12.0;HDR=Yes;IMEX=1")
- SQL = "select * from [DB$]"
- Set rs = DB.OpenRecordset(SQL)
- Sheet1.Range("B2").CurrentRegion
.ClearContents - With Sheet1.Range("B2")
- Do While Not rs.EOF
- .Offset(i, 0) = rs.Fields(0).Value
- .Offset(i, 1) = rs.Fields(1).Value
- .Offset(i, 2) = rs.Fields(2).Value
- .Offset(i, 3) = rs.Fields(3).Value
- rs.movenext
- i = i + 1
- Loop
- End With
- rs.Close
- DB.Close
- Set rs = Nothing
- Set DB = Nothing
- Set DBEngine = Nothing
- End Sub
329行目「Set DBEngine = CreateObject("DAO.DBEngine.120")」で、DAOのDBエンジンオブジェクトを生成します。
331行目「DBname = ThisWorkbook.Path & "¥" & ThisWorkbook.Name」で、接続するデータベース(今回はマクロのあるExcelファイルにDBがあるため、自Excelファイル名)を変数DBnameに代入します。
332行目「Set DB = DBEngine.OpenDatabase(DBname, False, False, "Excel 12.0;HDR=Yes;IMEX=1") 」で、データベースに接続します。この時第4パラメータConnectは「"Excel 12.0;HDR=Yes;IMEX=1"」としています。接続するデータベース種はExcel2007以降のため「Excel 12.0」にし、Excelシートデータベースの先頭行はタイトルとなっているため「HDR=Yes」としています。
読取/書込のIMEXは図09のように、データ型が混在している可能性も配慮し「IMEX=1」としています。なお、データ型の混在が無い場合は、IMEX値は0~2のどれでもOKですし、またIMEXを省略しても問題ありません。
334行目「SQL = "select * from [DB$]"」は、実行するSQL文(Select文)を変数SQLとしています。Excelのワークシートのデータをデータベースにする際は、テーブル名は「シート名+$印 を角カッコで囲む」必要があります。
335行目「Set rs = DB.OpenRecordset(SQL)」は334行目のSelect文を実行し、絞り込んだデータをレコードセットとして取得します。
その後の処理はOpenRecordsetにテーブル名を指定した時と全く同じです。
337行目「Sheet1.Range("B2").CurrentRegion
1レコード文の処理が完了したら、346行目「rs.movenext」でカーソルを移動させ、書き込む行位置も347行目「i = i + 1」で下に移動させます。
3ー2ー2-2.Insert・Update文の場合
実行するSQL文が「Insert」「Update」文の場合は、Executeメソッドを使用します。図37はデータを追加(Insert)するコードです。なおExcelシートをデータベースにする際は、「Delete文は使用不可」ですので注意が必要です。- '========== ⇩(14) ExcelデータにInset文で挿入 ============
- Sub DAO_Excel04()
- Dim DBEngine As Object '←DBエンジン
- Dim DBname As String '←データベース名
- Dim DB As Object '←データベース
- Dim SQL As String '←実行するSQL文
- Set DBEngine = CreateObject("DAO.DBEngine.120")
- DBname = ThisWorkbook.Path & "¥" & ThisWorkbook.Name
- Set DB = DBEngine.OpenDatabase(DBname, False, False, "Excel 12.0;HDR=YES;IMEX=0")
- SQL = "insert into [DB$] (St,Nu,Dt,BL) values('PQR',789,#2000/11/20#,False)"
' SQL = "update [DB$] set St='XYZ' where DT=#2000/11/20#"' SQL = "Delete from [DB$] where Dt = #2000/11/20#"←エラーが出ます- DB.Execute (SQL)
- DB.Close
- Set DB = Nothing
- Set DBEngine = Nothing
- End Sub
377行目「Set DBEngine = CreateObject("DAO.DBEngine.120")」で、DAOのDBエンジンオブジェクトを生成します。
379行目「DBname = ThisWorkbook.Path & "¥" & ThisWorkbook.Name」で、接続するデータベース(今回はマクロのあるExcelファイルにDBがあるため、自Excelファイル名)を変数DBnameに代入します。
380行目「Set DB = DBEngine.OpenDatabase(DBname, False, False, "Excel 12.0;HDR=YES;IMEX=0")」で、データベースに接続します。
この時第4パラメータConnectは「"Excel 12.0;HDR=YES;IMEX=0"」としています。接続するデータベース種はExcel2007以降のため「Excel 12.0」、Excelシートデータベースの先頭行はタイトルですので「HDR=Yes」としています。読取/書込のIMEXは図09のように「IMEX=0」又は省略とする必要があります。「IMEX=1 or 2ではエラー発生」しますので注意が必要です。
382行目「SQL = "insert into [DB$] (St,Nu,Dt,BL) values('ABC',789,#2000/10/20#,False)"」のSQL文は、新たなレコードを追加するものです。
そのSQL文を386行目「DB.Execute (SQL)」で実行することで、Excelシートへデータを追加します。
ここではレコードセットを開いていないので、閉じるのはデータベースのみで、388行目「DB.Close」のみを行います。
ここまではデータ挿入(Insert)の説明でしたが、見え消しにしてあるSQL文の
383行目「SQL = "update [DB$] set St='XYZ' where DT=#2000/11/20#"」
384行目「SQL = "Delete from [DB$] where Dt = #2000/11/20#"」
の内、Update文はレコード内容の変更が出来ますが、「Delete文はExcelシートを使うデータベースではエラー発生」します。
なおSQL文の構文などについては、他サイトや本サイトの「データを操作するSQL文」を参照下さい。
3ー2ー3.CSVデータの場合
3ー2ー3ー1.Select文
実行するSQL文が「Select」の場合は、OpenRecordsetメソッドにSQL文を指定し、絞り込んだレコードセットオブジェクトを取得した後、データ処理をします。- '========== ⇩(15) CSVデータをSelect文で取得 ============
- Sub DAO_CSV02()
- Dim DBEngine As Object '←DBエンジン
- Dim DBpath As String '←データベース(フォルダー名)
- Dim DB As Object '←データベース
- Dim SQL As String '←実行するSQL文
- Dim rs As Object '←レコードセット
- Dim i As Integer '←書き込み行位置
- Set DBEngine = CreateObject("DAO.DBEngine.120")
- DBpath = ThisWorkbook.Path
- Set DB = DBEngine.OpenDatabase(DBpath, False, False, "Text;HDR=Yes;")
- SQL = "select * from [its-041.csv]"
- Set rs = DB.OpenRecordset(SQL)
- Sheet1.Range("B2")
.CurrentRegion .ClearContents - With Sheet1.Range("B2")
- Do While Not rs.EOF
- .Offset(i, 0) = rs.Fields(0).Value
- .Offset(i, 1) = rs.Fields(1).Value
- .Offset(i, 2) = rs.Fields(2).Value
- .Offset(i, 3) = rs.Fields(3).Value
- rs.movenext
- i = i + 1
- Loop
- End With
- rs.Close
- DB.Close
- Set rs = Nothing
- Set DB = Nothing
- Set DBEngine = Nothing
- End Sub
409行目「Set DBEngine = CreateObject("DAO.DBEngine.120")」で、DAOのDBエンジンオブジェクトを生成します。
411行目「DBpath = ThisWorkbook.Path」で、接続するデータベース(今回はマクロのあるExcelファイルと同じ場所にCSVファイル(DB相当)があるため、自ファイルのパス名となる)を変数DBpathに代入します。
412行目「Set DB = DBEngine.OpenDatabase(DBpath, False, False, "Text;HDR=Yes;")」で、データベースに接続します。この時第4パラメータConnectは「"Text;HDR=Yes;"」としています。接続するデータベース種はCSVファイルのため「Text」にし、CSVファイルの先頭行はタイトルとなっているため「HDR=Yes」としています。
読取/書込のIMEXは省略していますが、これは図09でも分かる通り「CSV × SQL × Read 」の場合は、どれを設定しても同じ為です。尚データ型が混在している場合は、IMEXをどのように設定してもエラーが出ますので「CSVをデータベースにする際は、データ型を混在させない」事が必要です。
414行目「SQL = "select * from [its-041.csv]"」は、実行するSQL文(Select文)を変数SQLとしています。CSVファイルでのテーブル名は「ファイル名を角カッコで囲む」必要があります。
415行目「Set rs = DB.OpenRecordset(SQL)」は414行目のSelect文を実行し、絞り込んだデータをレコードセットとして取得します。
その後の処理はOpenRecordsetにテーブル名を指定した時と全く同じです。
417行目「Sheet1.Range("B2").CurrentRegion
1レコード文の処理が完了したら、426行目「rs.movenext」でカーソルを移動させ、書き込む行位置も427行目「i = i + 1」で下に移動させます。
3ー2ー3-2.Insert文の場合
実行するSQL文が「Insert」文の場合は、Executeメソッドを使用します。図39はデータを追加(Insert)するコードです。なおCSVファイルをデータベースにする際は、「Update、Delete文は使用不可」ですので注意が必要です。- '========== ⇩(16) CSVデータにInset文で挿入 ============
- Sub DAO_CSV03()
- Dim DBEngine As Object '←DBエンジン
- Dim DBpath As String '←データベース(フォルダー名)
- Dim DB As Object '←データベース
- Dim SQL As String '←実行するSQL文
- Set DBEngine = CreateObject("DAO.DBEngine.120")
- DBpath = ThisWorkbook.Path
- Set DB = DBEngine.OpenDatabase(DBpath, False, False, "Text;HDR=Yes;")
- SQL = "insert into [its-041.csv] (St,Nu,Dt,BL) values('PQR',789,#2000/11/20#,'True')"
' SQL = "update [its-041.csv] set St='XYZ' where DT=#2000/11/20#"←エラーが出ます' SQL = "Delete from [its-041.csv] where Dt = #2000/11/20#"←エラーが出ます- DB.Execute (SQL)
- DB.Close
- Set DB = Nothing
- Set DBEngine = Nothing
- End Sub
457行目「Set DBEngine = CreateObject("DAO.DBEngine.120")」で、DAOのDBエンジンオブジェクトを生成します。
459行目「DBpath = ThisWorkbook.Path」で、接続するデータベース(今回はマクロのあるExcelファイルと同じ場所にCSVファイル(DB相当)があるため、自ファイルのパス名となる)を変数DBpathに代入します。
460行目「Set DB = DBEngine.OpenDatabase(DBpath, False, False, "Text;HDR=Yes;")」で、データベースに接続します。この時第4パラメータConnectは「"Text;HDR=Yes;"」としています。接続するデータベース種はCSVファイルのため「Text」にし、CSVファイルの先頭行はタイトルとなっているため「HDR=Yes」としています。
読取/書込のIMEXは省略していますが、これは図09でも分かる通り「SQL × Read の場合は、どれを設定しても同じ」為です。
462行目「SQL = "insert into [its-041.csv] (St,Nu,Dt,BL) values('ABC',789,#2000/10/20#,'True')"」は、実行するSQL文(Select文)を変数SQLとしています。CSVファイルでのテーブル名は「ファイル名を角カッコで囲む」必要があります。
466行目「DB.Execute (SQL)」は462行目のInsert文を実行し、新たなレコードを追加します。
463~464行目では、Update文・Delete文も見え消しで紹介していますが、CSVファイルをデータベースとして使用する場合はUpdate・Deleteは使えませんので注意が必要です。
なお462行目のSQL文で「Boolean型のTrueは 'True'」としています。これは図32でも紹介しましたが、単にTrueやFalseとしてしまうと「-1」や「0」として保存されるのを防ぐためです。VBAではTrueは「-1」ですが、C言語やJava等ではTrueは「1」のようなので、CSVデータを他のプログラム言語でも使用できるように、文字列であったとしても「True」の方が対応し易いと思い、このようにしました。なおSQL文の中での文字列なので、両端は「'(シングルクォーテーション)」となります。
3ー3.データを一括で処理
ここまでは取得したレコードセットをDo~Loop等を使い「1レコード単位」でデータ処理をしましたが、一括で配列の形としてデータを受け取る事も可能です。Select相当だけに使える手法ですが、Accessを例にすると図40のようになります。なお、図40はOpenRecordsetにテーブル名を指定するコードですが、見え消しにしてあるSQL文でも使用できます。
- '========== ⇩(10) GetRowsでまとめてデータ取得 ============
- Sub DAO_Access04()
- Dim DBEngine As Object '←DBエンジン
- Dim DBname As String '←データベース名
- Dim DB As Object '←データベース
- Dim SQL As String '←実行するテーブル名・SQL文
- Dim rs As Object '←レコードセット
- Dim buf As Variant '←データの配列
- Set DBEngine = CreateObject("DAO.DBEngine.120")
- DBname = ThisWorkbook.Path & "¥" & "its-041.accdb"
- Set DB = DBEngine.OpenDatabase(DBname)
- SQL = "testtable"
' SQL = "select * from TestTable"- Set rs = DB.OpenRecordset(SQL, 2)
- rs.MoveLast
- rs.MoveFirst
- buf = rs.getrows(rs.RecordCount)
- With Sheet1.Range("B2")
- .CurrentRegion
.ClearContents - .Resize(UBound(buf, 1) + 1, UBound(buf, 2) + 1) = buf
- End With
- rs.Close
- DB.Close
- Set rs = Nothing
- Set DB = Nothing
- Set DBEngine = Nothing
- End Sub
544行目までは、図22と同じです。
539行目「Set DBEngine = CreateObject("DAO.DBEngine.120")」で、DAOのDBエンジンオブジェクトを生成します。
541行目「DBname = ThisWorkbook.Path & "¥" & "its-041.accdb"」で、接続するデータベースを変数DBnameに代入します。
542行目「Set DB = DBEngine.OpenDatabase(DBname)」で、データベースに接続します。
544行目「SQL = "testtable"」で、取得するテーブル名を変数SQLとします。
546行目「Set rs = DB.OpenRecordset(SQL, 2)」では、全データのレコードセットを取得していますが、第2パラメータTypeに「dbOpenDynaset(値=2)」を指定しています。このパラメータ値の設定範囲については後述します。
546行目でレコードセットを取得した状態では、レコードセットのカーソル位置は先頭行にあります。548行目「rs.MoveLast」でカーソルを一旦最終行まで移動させた後、549行目「rs.MoveFirst」で再びカーソルを先頭に戻しています。
そして550行目「buf = rs.getrows(rs.RecordCount)」でGetRowsメソッドを使って、全データを一気に取得し変数bufに配列として代入しています。
なお見え消しにしていますが、545行目「SQL = "select * from TestTable"」を使えば、絞り込んだテーブルが一括で配列化できる事になります。
GetRowsメソッドに関して詳しく説明していきます。まず構文は以下のように「取り出す行数」をパラメータ指定します。
レコードセット.GetRows(取得行数)
但しGetRowsメソッドを使うに当たり注意点が3つあります。
1: | パラメータ指定する「取り出す行数」を省略すると「1」を指定した事になる。 OpenRecordsetメソッド実行直後はカーソルが先頭にあり、指定するパラメータによってはレコードセットの「総レコード数(RecordCountプロパティ値)」が不明な状態となります。 |
2: | カーソルのある行位置がら後ろ側が取得対象となる。 例えばMoveNextやMoveLastメソッド等でカーソルを動かしてしまうと、全データが取得できないことになります。 |
3: | GetRowsで取得したデータ配列は「行列が逆転した配列」になる。 Transposeメソッドで行列を回転させる事は可能ですが、要素内に「Nullが入っているとエラー」が発生します。また1行しか取得できなかった場合に回転させると、二次元配列が一次元配列に変わってしまいます。 |
以上の特性を踏まえると、総レコード数を超える「とても大きな値」を指定してGetRowsメソッドを実行する手法も考えられます。しかしカーソルを一旦動かすとレコードセットの総レコード数を取得できるという特性を使い、「GetRowsメソッドに総レコード数を指定」する方法を今回は使用します。
カーソルを移動させることで得られる「行数」を整理したのが図41です。
「移動前」がレコードセットを取得した直後を示し、その状態から「MoveLast」メソッドを実行した後が隣の列、レコードセット取得後「MoveNext」メソッドを実行した後がその隣の列・・・という具合に見て下さい。
定数 | 値 | 移 動 前 | →Move | →Move | →Get |
---|---|---|---|---|---|
dbOpen | 1(Table指定の既定) | (全行数) | 全行数 | 全行数 | 全行数 |
dbOpen | 2(SQL文指定の既定) | 1 | 全行数 | 全行数 | 全行数 |
dbOpen | 4 | 1 | 全行数 | 全行数 | 全行数 |
dbOpen | 8 | 1 | × | 2 | 2 |
図41のように、OpenRecordsetメソッドに指定するTypeパラメータにより、得られる行数が変わってくる事が分かります。なお移動前の「(全行数)」は、テーブルを指定(図17参照)した時は全行数が得られるのですが、SQL文を指定(図19参照)した場合は全行数が得られないことから、カッコ付としています。
全行数が得られる条件をまとめると、以下のようになります。
OpenRecordsetメソッドの第1パラメータに「テーブル名を指定」した場合は、第二パラメータの既定値はType=1ですので、レコードセットを取得した時点でそのまま全行数が取得できます。
一方第1パラメータに「SQL文を指定」した場合は、第二パラメータの既定値はType=2となり、そのままでは全行数が取得できません。しかし一旦カーソルを動かしておく事で全行数が取得できます。
つまり、第1パラメータが「テーブルでもSQL文でも成立」するためには、第二パラメータをType=2、またはType=4 とすることが必要です。その上でカーソルを一旦動かした後、カーソルを先頭に戻すようにします。
なお「Type=2」「Type=4」のどちらでも良さそうですが、図17・図19の表内で「LockEditsプロパティ」が使用できる方が安全と判断し、今回は「Type=2」を使用しました。
なおカーソルの動かし方については色々なパターンが考えられます。「MoveNext → MovePrevious」「GetRows → MoveFirst」等等ですが、今回はその内の「MoveLast → MoveFirst」を使っています。
図40のコードでは、OpenRecordsetの第2パラメータを「dbOpenDynaset(値=2)」にしてレコードセットを取り出し、「MoveLast → MoveFirs」と続けて実行することでRecordCount値を全行数に変更した後、GetRowsメソッドで全データを一括取得しています。但し3つ目の注意点として、GetRowsメソッドで得られるデータは「行列が逆転した配列」であることです。
Transposeで行列変換をすることは可能ですが、データ内に「Null」が存在すると、変換時にエラーが発生してしまいます。今回は行列逆転したままの形でワークシートに貼り付けています。
552行目「With Sheet1.Range("B2")」で、書き出しの位置基準を示し、553行目「.CurrentRegion
4.Accessデータの作成について
今回のサンプルコード用のAccessデータベースを作るマクロを以下で紹介します。詳細については「ExcelからAccessデータベースを作成・操作」を参照下さい。ファイルを作るには、図42を実行して下さい(サンプルファイルでは、Module2にコードを記載しています)。なおファイル名重複などのエラー処理はしていませんので、作り直す時は手動でAccessファイルを削除してから再実行して下さい。
- '========== ⇩(14) Accessサンプルデータ作成 ============
- Sub make_Access_sampleData()
- Call make_AccessDB
- Call make_AccessTable
- Call make_AccessData
- End Sub
まず602行目「Call make_AccessDB」で図43を呼び出し、Accessファイル(≒データベース)を作成します。
次に603行目「Call make_AccessTable」で図44を呼び出し、テーブルを作成します。
最後に604行目「Call make_AccessData」で図45を呼び出し、レコードを4つ挿入します。
4ー1.Accessファイルの作成
図43では、Accessファイルそのものを作成しています。- '========== ⇩(14) Accessファイル作成 ============
- Sub make_AccessDB()
- Const DBname As String = "its-041.accdb" '←DBの名前
- Dim DBpath As String '←DBの場所
- Dim Cat As Object '←Catalogオブジェクト
- DBpath = ThisWorkbook.Path & "¥"
- Set Cat = CreateObject("ADOX.Catalog")
- Cat.Create "Provider=Microsoft.Ace.OLEDB.12.0;" & _
- "Data Source=" & DBpath & DBname
- Set Cat = Nothing
- End Sub
612行目「Const DBname As String = "its-041.accdb"」では、作成するAccessファイル名を定数宣言します。
616行目「DBpath = ThisWorkbook.Path & "¥"」では、作成場所を指定します。今回はこのExcelと同じ場所にしています。
618行目「Set Cat = CreateObject("ADOX.Catalog")」では、ADOXのCatalogオブジェクトを作成しています。
620~621行目「Cat.Create "Provider=Microsoft.Ace.OLEDB.12.0;Data Source=" & DBpath & DBname」では、ADOXのCreateメソッドで「新規データベースファイル(≒Accessファイル)」を作成します。この際のProviderにはAceを使用し、場所はExcelファイルのあるフォルダー(変数DBpath)に、ファイル名は「its-041.accdb」という名前(定数DBname)で作成します。
4ー2.Accessテーブルの作成
図44では、データベース(≒Accessファイル)内にテーブルを作成しています。テーブルの構成は図20の通りです。- '========== ⇩(14) Accessテーブル作成 ============
- Sub make_AccessTable()
- Const DBname As String = "its-041.accdb" '←DBの名前
- Const Tname As String = "TestTable" '←作成するテーブル名
- Dim SQL As String '←SQL文
- Dim cn As Object '←Connectionオブジェクト
- Dim DBpath As String '←DBが存在する場所
- DBpath = ThisWorkbook.Path & "¥"
- Set cn = CreateObject("ADODB.Connection")
- cn.Provider = "Microsoft.ACE.OLEDB.12.0"
- SQL = "Create Table " & Tname & " (" & _
- "St LONGTEXT," & _
- "Nu DOUBLE," & _
- "Dt DATETIME," & _
- "BL BIT" & _
- ")"
- cn.Open DBpath & DBname
- cn.Execute SQL
- cn.Close
- Set cn = Nothing
- End Sub
632行目「Const DBname As String = "its-041.accdb"」では、データベース名を定数宣言しています。
633行目「Const Tname As String = "TestTable"」では、テーブル名を定数宣言しています。
638行目「DBpath = ThisWorkbook.Path & "¥"」はデータベース(≒Accessファイル)の場所を指定しています。
640行目「Set cn = CreateObject("ADODB.Connection")」でADOのConnectionオブジェクトを生成します。
641行目「cn.Provider = "Microsoft.ACE.OLEDB.12.0"」で、プロバイダはACEとします。
643~648行目でテーブルを作成するSQL文(Create Table文)を作成します。
まず643行目「SQL = "Create Table " & Tname & " (" & _ 」で、テーブル名を「TextTable(変数Tname)」とします。
644行目「"St LONGTEXT," & _」は1列目の列名を「St」、データ型を「LONGTEXT(VBAでString型相当)」にします。
645行目「"Nu DOUBLE," & _」は2列目の列名を「Nu」、データ型を「DOUBLE(VBAでDouble型相当)」にします。
646行目「"Dt DATETIME," & _」は3列目の列名を「Dt」、データ型を「DATETIME(VBAでDate型相当)」にします。
647行目「"BL BIT" & _」は4列目の列名を「BL」、データ型を「BIT(VBAでBoolean型相当)」にします。
650行目「cn.Open DBpath & DBname」ではデータベース(≒Accessファイル)を開きます。
651行目「cn.Execute SQL」では、643~648行目で作成したSQL文を実行し、テーブルを作成します。
4ー3.Accessレコードの作成
図45では、テーブル内にレコードを作成しています。- '========== ⇩(14) Accessレコード作成 ============
- Sub make_AccessData()
- Const DBname As String = "its-041.accdb" '←DBの名前
- Const Tname As String = "TestTable" '←テーブル名
- Dim DBpath As String '←DBの場所
- Dim cn As Object '←Connectionオブジェクト
- Dim SQL(1 To 4) As String '←SQL文(4種)
- Dim i As Integer '←カウンタ変数(列数)
- DBpath = ThisWorkbook.Path & "¥"
- Set cn = CreateObject("ADODB.Connection")
- cn.Provider = "Microsoft.ACE.OLEDB.12.0"
- SQL(1) = "insert into " & Tname & _
- " (St,Nu,Dt,BL)" & " values('abc',123,#2023/1/2#,True)"
- SQL(2) = "insert into " & Tname & _
- " (St,Nu,Dt,BL)" & " values('def',123,#2023/2/2#,False)"
- SQL(3) = "insert into " & Tname & _
- " (St,Nu,Dt,BL)" & " values('abc',456,#2023/3/2#,False)"
- SQL(4) = "insert into " & Tname & _
- " (St,Nu,Dt,BL)" & " values('def',456,#2023/4/2#,True)"
- cn.Open DBpath & DBname
- For i = 1 To 4
- On Error Resume Next
- cn.Execute SQL(i)
- If Not Err.Number = 0 Then MsgBox Err.Description
- On Error GoTo 0
- Next i
- cn.Close
- Set cn = Nothing
- End Sub
662行目「Const DBname As String = "its-041.accdb"」では、データベース名を定数宣言しています。
663行目「Const Tname As String = "TestTable"」では、テーブル名を定数宣言しています。
669行目「DBpath = ThisWorkbook.Path & "¥"」はAccessファイルの場所を指定しています。
671行目「Set cn = CreateObject("ADODB.Connection")」でADOのConnectionオブジェクトを生成します。
672行目「cn.Provider = "Microsoft.ACE.OLEDB.12.0"」で、プロバイダはACEとします。
674~681行目では、挿入レコードのSQL文を作成しています。今回は4つのSQL文(String型)を配列SQLに格納し、685~690行目のFor~Nextを回しながらレコード挿入をしていきます。
674~675行目「SQL(1) = "insert into " & Tname & " (St,Nu,Dt,BL)" & " values('abc',123,#2023/1/2#,True)"」が1個目のSQL文で、図44で作成したテーブルに新たなレコードを挿入(Insert)しています。
他の3個のSQL文もほぼ同じで、挿入する値を少しずつ変えています。
683行目「cn.Open DBpath & DBname」で、Accessファイルを開きます。
685行目「For i = 1 To 4」でカウンタ変数iを回しながら、687行目「cn.Execute SQL(i)」で配列に格納したSQL文を1つずつ実行(=レコード挿入)していきます。
なお、もしExecute実行時にエラーが出た際には、その内容が分かるように688行目「If Not Err.Number = 0 Then MsgBox Err.Description」でエラー内容(Err.Description)をメッセージ出力するようにしています。
アプリ実例・関連する項目
「ExcelからAccessデータベースを作成・操作」サンプルファイル
DAOでAccess・Excel・CSVへ接続・操作(its-041.xlsm)Accessデータファイル(its-041.accdb)
CSVデータファイル(its-041.csv)
セキュリティ向上を目的として「インターネット経由でダウンロードしたOfficeファイル(Excel等)のマクロは、既定でブロック」されるようにOfficeアプリケーションの既定動作が変更になりました。(2022年4月より切替開始) 解除の方法については「ダウンロードファイルのブロック解除方法」を参照下さい。 |
|