2024/01/22

ODBCでAccess・Excel・CSVへ接続・操作




1.データベースへの接続方法

DAOでAccess・Excel・CSV等へ接続・操作」でも説明しましたが、ExcelのVBAから「Microsoft Access」「Excelのワークシート」「CSV等のテキストファイル」のデータベースに接続・操作する方式には、主に「ODBC」「DAO」「ADO」の3種があります。
今回はDAOやADOを使用せず、ODBCを使ってデータベースにアクセスするルート(図01では黒線)を説明します。
ODBC方式のルート
図01

DAOやADOを使用しない方法として、調べた限りでは以下のようにいくつかの手段があるようです。
 ・QueryTable
 ・ListObject
 ・Power Query
この内「Power Query」は、Excel2016から標準搭載された機能です。このPower Queryは、図02で示すようなExcel上のリボンの「データ」タブをクリックして現れる「データの取得」ボタンの裏で動いている手法でもあるようです。
(確認として、このボタン操作の一部を「マクロの記録」でコード化してみました。その中には「Provider=Microsoft.Mashup.OleDb.1;」という部分が見つかります。これは Power Queryで接続している事を示しているようです。)
Excelでのデータベースへの接続方法
図02

Power Queryは「M言語」というVBAとは異なる知識が必要となるので今回は対象から除外し、以下では「QueryTable」と「ListObject」を使ったデータベース接続・操作について説明をしていきます。
このQueryTableとListObjectによるODBC接続では、データの取り込み(Select文)だけで無く、データ変更(Insert、Update、Delete文)も可能ですので、ListObject操作などと合わせて使用すると有用なツールになると思われます。
なお、これ以外(DAO、ADO)の接続・操作については、以下を参照下さい。
 ・DAOでAccess・Excel・CSVに接続・操作
 ・ADOでAccess・Excel・CSVへ接続・操作(JET・ACEプロバイダー編)
 ・ADOでAccess・Excel・CSVへ接続・操作(MSDASQLプロバイダー編)
 ・ODBCでAccess・Excel・CSVへ接続・操作  ←今回
また、今回は「ADOでAccess・Excel・CSVへ接続・操作(MSDASQLプロバイダー編)」と同じODBC系であるため、重複する部分が多くあることをご了承下さい。

2.ODBCについて

まず、ODBC(Open Database Connectivity)について説明をします。
ODBCとは、Microsoftが作ったデータベースに接続する手順です。データベースには多くの種類がありますが、それぞれに対応したODBCドライバーを使うことで、ユーザー側は同じようなコードで様々なデータベースにアクセスできます。
ODBCの設定をするには、Windowsの「コントロールパネル」から入ります。
図03のように、コントロールパネルの「システムとセキュリティ①」→「Windowsツール②」を選択すると、その中にODBCのアイコン③が現れます。(Windowsのバージョンにより、少し違うかもしれません)
ODBCへのアクセス方法1
図03

また、コントロールパネルの右上の検索ボックスに「ODBC」と入力④する事で、図04の右図のようにODBC⑤を絞り込む事ができます。
ODBCへのアクセス方法2
図04

ODBCの設定アイコンには、図03の③・図04の⑤のように、最近では32ビット版と64ビット版の2つがありますが、どちらを使うかは「使用するアプリケーション(今回だとExcel)のビット版」で決まります。つまり、Excel(≒Office)が64ビット版であれば「ODBC(64ビット)」を、32ビット版であれば「ODBC(32ビット)」を選択します。
ODBCの設定アイコンを開くと、図05のように、いくつかのタブが並んでいます。今回は「ユーザーDSN」タブと「ドライバー」タブを使用します。
ODBCのダイアログ内部
図05

なお「ユーザーDSN」と「システムDSN」は内容的には同じですが、PC内に複数のユーザーがいる場合「ユーザーDSN」は現在ログインしているユーザーのみが使え、「システムDSN」は全ユーザーが共通して使えるもの という違いがあります。
但し、システムDSNに作成したDSNが表示されない(見ることが出来ない)状況になる条件があるようです。実際、私のPCでも見ることが出来ません(レジストリには記録されている)でした。
なお図05は64ビット版のものですが、32ビット版でもほぼ同じ内容です。
また、64ビット版Excelの入ったPCで32ビット版のODBCを調べると、昔のファイル(例えば、Accessの *.mdb)にしかアクセスできないように見えます。しかし32ビット版Excelの入ったPCで32ビット版ODBCを調べると、ちゃんと最近のファイル(*.mdb と *.accdb)にもアクセスできるドライバーが入っていることがわかります。つまり「Excelのビット版に合わせたドライバーがインストール」されていますので心配ありません。
(必要なドライバーが入っていない場合もあるようなので、確認は必要と思います。)

2-1.ODBCドライバー

使用可能なODBCドライバーは、図05の右側のようにリストになっていますので、その中から選択し、QueryTableやListObjectの作成時に指定します。指定方法は「"Driver=Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb);"」のように「ドライバー名を指定した文字列」を作って指定します。
なお、ドライバー名をコード上に指定する際には、以下のことに注意します。
・文字列は、カッコ内も含めて正しく書き写す
・スペースの位置と数も正しく書き写す
・大文字小文字は同じ文字と認識される
・半角と全角は異なる文字と認識される
基本的にドライバー名には全角文字は使われていないと思いますが、スペースが入っているか否かは見落とし勝ちです。レジストリエディタで検索をすればコピペも可能ですが、普通の方法ではコピペが出来ないので、良く見て書き写すことが必要です。

2-2.DSN

DSN(Data Source Name)は、簡単に言えば「ODBCドライバーとデータベースファイル名をセット」にしたものです。ODBCでデータベースに接続する際は、最低でもドライバー名とデータベースファイル名を指定する必要がありますが、この代わりにDSNを指定すれば接続できることになります。
図05の左図のように「DSNタブ」を開くと、すぐに使用可能なDSNが並んでいます(但しデータベースファイル名は、初期には未設定)。このダイアログでは、図06のようにDSNを新作したり、内容を修正したりすることができます。
DSNの作成
図06

DSNリストの右には3個のボタンが並んでいます。
一番上の「追加①」ボタンは、新たなDSNを作成するものです。まず最初にODBCドライバーを指定④(図06の右上)し完了ボタンをクリックすると、次にDSN(データソース名)を入力⑤するダイアログ(図06の右下)になります。
リストからDSNを選択した後「構成③」ボタンを押しても、名前を変更⑤できます。
二番目の「削除②」ボタンは、リスト選択したDSNを削除するものです。例えばDSNにデータベースファイルを一旦設定してしまうと、データベースファイルが空の状態の初期DSNには戻せません。その場合は一旦削除②してから新規に作成①するしか無いようです。
三番目の「構成③」ボタンをクリックすると、以下(図07など)で説明するように、データベースファイル名などを設定することができます。
なおこの構成ダイアログの内容はドライバー毎に異なります。
寄り道(DSNの名前)
図06の⑤に設定するDSN(データソース名)ですが、プログラム側から呼び出す際は「大文字小文字は同一」と判断されるようです。
また名前の区切り等に使う「スペース」は、図06の左図のリストでも分かるように「名前の文字列の間」に入れることは大丈夫そうです。しかしスペースを文字列の先頭や末尾に入れてしまうと、構成のダイアログが開かなくなり編集不可となりますので注意が必要です。
(レジストリエディタで削除する事は可能ですが、失敗するとWindowsが起動しなくなる危険性があります。)

2-2-1.AccessのDSN

Accessのドライバーを選定した時の構成ダイアログは、下図(図07)のようになります。
DSNの変更(Access)
図07

Access構成ダイアログでは、まずデータベースの「選択①」ボタンをクリックし、データベースファイルを設定⑤します。
また「詳細設定③」ボタンをクリックすると、新たなダイアログ⑧が開き、Accessファイルに対するログイン名・パスワード設定や、各設定値の変更が可能です。リスト⑨の各項目を選択すると、下部⑩に項目名と値のボックスが出ますので編集できるようになっています。
右下部の「オプション④」ボタンではダイアログが下に伸び、ページタイムアウトやバッファサイズ等の変更ができます。
なお、「作成②」ボタンから、新しいAccessデータベースファイルを作成⑥することも可能です。その際は、バージョンを指定⑦する必要があり、「バージョン4.xでは、*.mdbファイル」が作成され、「バージョン12.xでは、*.accdb」ファイルが作られます。
mdbファイルにはJetエンジンで作られるものと、ACEエンジンで作られるものの2種がありますが、確認したところ「Jetエンジンで作られるmdbファイル」のようです。
また、ワークグループ情報(データベースへアクセスするためのIDやPWなど)をファイル化したシステムデータベース(System.mdw)がある場合は、システムデータベースの「データベース⑬」のラジオボタンをクリックし「システムデータベース⑫」ボタンをクリックすることで、新たなダイアログ⑬が表示されファイル選択ができます。

2-2-2.ExcelのDSN

ExcelのDSNについて下図(図08)で説明します。
DSNの変更(Excel)
図08

Excelの構成ダイアログでは、まずデータベースのバージョン①をドロップボックス④から選択します。既定はExcel 12.0(Excel2007~)のようです。
それ以前のバージョンも選択できますが、「Excel 3.0、及び Excel 4.0」では、ブックの選択ボタンが「フォルダーの選択」ボタンへと名称が変わり、フォルダーしか選択できなくなります。ちょうど現在のCSVファイルのような立場だったのかもしれません。またExcel2003が抜けていますが、「Excel 97-2000」「Excel 12.0」のどちらでも大丈夫そうです。
バージョンの選択が完了したら「ブックの選択②」ボタンをクリックし、ダイアログ内⑤を操作してExcelブックを選択します。
もしかしたらバージョン選択④の目的は、このダイアログ⑤に於ける表示拡張子を決める役目なのかもしれません。
右下の「オプション③」ボタンをクリックすると、ダイアログが下に伸び⑥、「スキャンする行数」の入力と「読み取り専用」有無の選択が可能です。
この「読み取り専用」にレ点を入れると、Insert文などの実行時にエラーが発生します。但し、読み取り専用に設定したDSNを指定するのと同時にコード側で「ReadOnly=0(読取専用ではない=書き込みOK)」 の指定をすると、Insert等も可能になります。逆はダメなので、コード側の指定が優先されるようです。
「スキャンする行数」は、データ列のデータ型を判定するためのスキャン行数(MaxScanRows)です。既定値は「8」ですが、値を変更しても処理内容は変わりません(他の複数サイトでも解説されていますが、このMaxScanRows設定はバグにより無効となっているようです)。
また「ゼロを設定すると全行をスキャン」との説明も見受けられますが、ここではゼロを入力する事はできません。

2-2-3.テキスト(CSV)のDSN

CSV(テキストファイル)を選定した時の構成ダイアログは、下図(図09)のようになります。
DSNの変更(CSV)
図09

テキストファイルの場合は、データベース相当は「テキストファイルがあるフォルダー」となります。ですので、テキストの構成ダイアログでは「現在のフォルダーを使用する①」というチェックボックスをまず確認します。
チェックボックスにレ点がついていると、ODBCが置いてある「C:¥windows¥system32」辺りになってしまいますので、通常はレ点を外し「フォルダーの選択②」ボタンをクリックすることでダイアログ④が表示され、フォルダーを選択します。
右下の「オプション③」ボタンをクリックするとダイアログが下に伸び、テキストファイルの拡張子の追加/削除ができるようになります。
テキストファイルのあるフォルダーを指定④し、ファイルの拡張子を指定⑤した後で「書式の定義⑥」ボタンをクリックすると、テキスト形式の定義ダイアログ⑦が現れます。そのダイアログの左上リストボックス(「テーブル」と表示されている)には「指定したフォルダー内に存在する、指定した拡張子のファイル」が一覧表示されます。テキストデータベースにしてみれば、これが「テーブル」相当になります。
リスト上のファイル(テーブル相当)を選択し、右側の「推量⑨」ボタンをクリックすると、右のリストボックスに「列の一覧」が表示されます。先頭行が列名である場合には「列名の見出し⑧」にレ点を付けておけば、ちゃんとした列名となります(列名の見出し⑧にレ点が付いていないと、F1・F2 ・・・等と表示されます)。
列のリストを選択すると、右リスト下にデータ型・名前・幅が表示されます。データ型は、左側の「スキャンする行数⑩」の行数だけ読み込んで、数の多いデータ型を表示しているようです。ちょっと便利そうな機能です。
寄り道(テキストの列情報の編集)
図09のダイアログ⑦の右下の「列情報」の右端には、列を追加・変更・削除できるボタンも並んでいます。実際に追加ボタン等を使ってみると変更されているように見えますが、実際のファイル内で列が追加できたり、削除されたりは無さそうです。しかしその情報は、指定したフォルダー内の「schema.ini」内に保存されている(存在しなければ新規作成される)ようで、再度呼び出すと変更内容は記憶されています。
但し、データ型などを一旦変更してしまうと、その保存された情報による悪影響が発生し、データを操作しようとすると「抽出条件でデータ型が一致しません」などのエラーが発生する事があります。もしそうなってしまった場合は「schema.ini」を削除するのが良いと思います(編集も出来そうですが、今回は省略します)。

3.接続文字列について

今回は、QueryTable または ListObject を通してデータベースに接続をします。接続をする際、それぞれのオブジェクトのAddメソッドを実行し、それぞれのオブジェクトを作成しますが、データベースへの接続文字列はAddメソッドのパラメータの1つに指定することになります。以下ではそれぞれのAddメソッドのパラメータのどこに接続文字列を指定するかを比較しています。
QueryTables.AddListObjects.Add
パラメータ名内容パラメータ名内容
1Connection接続文字列 ①SourceType接続種
2Destination出力セル位置 ②Source接続文字列 ①
3SqlSQL文LinkSourceリンク有無
4--XlListObjectHasHeadersデータのタイトル有無
5--Destination出力セル位置 ②
6--TableStyleNameテーブル名
図10

図10で分かるように、データベース接続文字列①は、
QueryTablesでは第1パラメータ(Connection)ListObjectsでは第2パラメータ(Source) に指定します。
パラメータ名は異なりますが、試してみると接続文字列は、どちらのオブジェクトに対しても同じ書式の指定で問題無さそうですので、共通項目として以下で説明していきます。
なお、お気付きのように「データの出力セル位置②」も、QueryTableとListObject とで同じDestinationパラメータを使用します。但し、確かに設定内容は同じなのですが、動作(タイトル行表示など)が2つのオブジェクト間で少し異なるので、詳細については各オブジェクトの説明の中で紹介しています。

3-1.接続文字列の内容

QueryTables.Addでは第1パラメータ(Connection)に、ListObjects.Addでは第2パラメータ(Source)に指定するのが、データベースに接続するための接続文字列です。
接続情報の内容としては、図11のようになります。
接続情報
内容ODBC
ドライバーDriver={ODBCのドライバー名}
データベース名DBQ=データベース名
DSN名DSN=DSN名
ユーザー名UID=ユーザー名
パスワードPWD=パスワード
読取/書込ReadOnly=1(True)/0(False)
図11

ODBCの接続情報として必須なのは「ドライバー名」+「データベース名」ですが、その代わりに「DSN名」を指定する方法もあります。その組み合わせを図12にまとめました。
ドライバ、データベース、DSNの組み合わせ
図12

1つ目の方法(図12の①)は、「ODBCドライバー」と「データベース」を指定する方法です。ODBCドライバーには、図05の右側で示したドライバーリストから、種類に合った1つを指定します。データベースには、AccessやExcelのファイル名を、CSVファイルの場合はフォルダー名を指定します。
2つ目の方法(図12の②)は、「DSN」で説明したDSN(Data Source Name)を指定する方法です。DSNを作成するにはドライバーの指定が不可欠ですので、それに加えてデータベースを事前に設定しておけば、ドライバーとデータベースの両方が指定されている事になり、DSNの指定のみでOKとなります。
3つ目の方法(図12の③)は、データベースが未設定のDSNの指定と併せて、データベースを別個に指定する方法です。この手法でも、ドライバーとデータベースの両方が指定されていることになるのでOKです。
なお、データベース設定済みのDSNに対してこの方法を使うと、DBQ=で設定するコード上のデータベースの方が優先されます。

3ー1ー1.ドライバー名

ドライバーには、対象となるデータベース種に対応したものを選択します。且つ、そのドライバーを使うアプリ(今回だとExcel)のビット数に合ったものにする必要があります。
64ビット版のWindowsには、64ビット版ODBCと32ビット版ODBCがありますが、例えば私の64ビット版Officeが入っているPCでは、図13のように、ドライバー種はかなり違います。
64ビットOfficeの入っているPCのODBCドライバー
図13

一方、32ビット版Officeが入っているPCでは図14のようになります。
32ビットOfficeの入っているPCのODBCドライバー
図14

図13図14を比較してみると、Officeのビット版と同じビット版のドライバー側に「必要とするドライバー」が入っていることが分かります。Officeがインストールされる際に、それに関連するドライバーもインストールされている様です。
且つ「ドライバーの名称は、32ビット版も64ビット版も同じ」ようなので、ビット版が混在する複数のPCに対してのプログラムでも、ビット版を意識せずに同じコードで動くと言えます。
なお、ExcelとAccessについては、ドライバーは下記の1種類ずつなので、一意に決まりそうです。
 ・Excel :Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)
 ・Access:Microsoft Access Driver (*.mdb, *.accdb)
一方CSVファイル用のドライバーは、色々な種類がありそうですが、CSVと言えども「Officeのビット版と同じビット版のODBCに入っているドライバー」のみが対応できます。つまりCSV(テキストファイル)には、以下のドライバーを使うのが安全です。
 ・CSV :Microsoft Access Text Driver (*.txt, *.csv)
なお、32ビット版のOfficeであるならば「Driver da Microsoft para arquivos texto (*.txt; *.csv)」等でも動きます。但しスペルに気を付ける事と、カンマの代わりに「;(セミコロン)」を使っている事にご注意下さい。
このドライバー名を指定する場合は以下のように、ドライバー名を波かっこ( { } )で括り、「Driver=」の後ろに続けます。
 "Driver={Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)};" など

3ー1ー2.データベース名

データベース名は、対象とするデータベースの種類により図15のように変わります。
データベース名
Accessファイル名(*.accdb, *.mdb)
Excelファイル名(*.xlsx 等)
CSV等CSVファイルがあるフォルダー名
図15

AccessやExcelのように、ファイル内に複数のテーブルを持てるタイプでは「ファイル名」がデータベース名となりますが、CSVファイルのように ファイル=テーブル のようなものは「フォルダー名」がデータベース名となります。
データベースを指定するには「DBQ=」を使用し、例えば以下のようにします。
 ・Access:"DBQ=C:¥Data¥its-041.accdb;"
 ・Excel :"DBQ=C:¥Data¥its-041.xlsm;"
 ・CSV :"DBQ=C:¥Data¥;"
なお、CSVの文字列最後の¥印は、あっても無くても変わりません。
なおAccess,Excel,CSVはでは、上記のように「DBQ=」を使用してデータベースを指定しますが、SQL Serverでは「DATABASE=」を使用するようです。

3ー1ー3.DSN名

ドライバーとデータベースの情報を1つにまとめた「DSN」名をODBCダイアログから選んで指定します。
図16は左右図とも私のPCのものですが、64ビット版OfficeがインストールされたPCですので、同じ64ビット版のODBC(左側)からDSNを選ぶことになります。
64ビットOfficeの入っているPCのDSN
図16

ODBCを初めて使用する状態では、図16のように「Excel File」と「MS Access Database」は既定で作られています(違う場合もあるとは思います)ので、データベースファイルを設定すれば、すぐ使えることになります。しかしCSV等のテキストファイルについては「テキストのDSN」などを参考に元から作る必要があります。
一方、32ビット版Officeが入っているPCでは図17のようになります。
32ビットOfficeの入っているPCのDSN
図17

図16図17を比較してみると、64ビット版も32ビット版も、同じ名前でExcelとAccessのDSN(データベースは未設定)は作られている事が分かります。ですので図12の③のパターンであれば、面倒なドライバー名を書き写さなくても、複数人に配布するプログラムに盛り込めるとも言えます。
しかし、このDSNはユーザー自身が簡単に名前変更などが可能ですので、「DSNの変更禁止」を徹底しておく必要があります。
また同様に、データベースを設定したDSNを使う場合も、1台1台のPCへのDSN設定が必要になりますし、例えそれが可能だったとしても、ユーザー自身によるDSN変更の懸念は無くなりません。
まとめると、DSNを使うとプログラムが単純になりますし、オプション設定等もありますので、個人で使うにはメリットが大きいと思います。しかし複数人で同じプログラムを使う場合は、図12の①の「ドライバー設定 + データベース設定」の方が安全だと考えます。
なおDSN設定時は、"DSN=Excel Files;" 等の文字列としてAddメソッドのパラメータに指定します。

3ー1ー4.ユーザー名、パスワード

データベースに「ユーザー名」や「パスワード」が設定されている場合は、「UID=」や「PWD=」に続けて指定をし、接続情報の文字列に加えます。なお、Excelの「ブックの保護」で設定したパスワードは対象外です。(「PWD=ブック保護のパスワード」を指定しても、データは取得できません)
この接続情報の確認のために、ユーザー名・パスワードを「ExcelのVBA側で作成したAccessファイル」に設定しようと試みました。やり方としては、ADOXのUsersコレクションから、NameプロパティとChangePasswordメソッドを使うと思われますが、残念ながらExcel VBAからは操作できませんでした。
実際のAccessで作成されたデータベース(*.accdb, *.mdb)の場合は当然設定が可能だと思います。

3ー1ー5.読取/書込

データベースに対して、読み取り専用とするには「"ReadOnly=1"」、書き込みOKとするには「"ReadOnly=0"」とします。
なお、読み取り専用を狙って「"ReadOnly=True"」とか「"ReadOnly=" & True 」のように指定しても、残念ながら読み取り専用にはなりません。後者は、Excel VBA上での文字列としては 「"ReadOnly= -1"」と解釈されてしまいます。
読み取り専用にするには必ず「"ReadOnly=1"」とすることが必要で、それ以外は全て書き込みOKの「"ReadOnly=0"」という扱いになるようです。
注意を要するのはスペースで「"ReadOnly =1"」のように「イコール記号の前にスペースを入れる」と「ReadOnly=0(書き込みOK)」扱いとなります。ちなみに「"ReadOnly=False"」と指定すると、"False"は文字列なのでゼロ値とみなされるため、ReadOnly=0 と解釈されるようです。
またReadOnlyを省略したときの既定値ですが、以下のようにドライバー毎に異なります。
ドライバーReadOnly= 省略時
AccessFalse(書込可)
ExcelTrue(読込のみ)
Text(CSV)False(書込可)
図18

Excelだけは「省略するとReadOnly=1(True)」なので、書き込みが必要な場合は「ReadOnly=0;」の記述が必要です。
またAccessとCSVは、省略すると書き込み可となります。ですので書き込みさせたくない場合は「ReadOnly=1;」とすれば良いのですが、基本的には「常にReadOnly=〇〇; を記述」し、データベースへの処理を明確にするのが大切と思います。
寄り道(HDRなどの設定)
HDR(データの先頭行がタイトルか否か)、IMEX(読取/書込)、FMT(データ区切り記号)の設定等は、ACE・Jetプロバイダーでは「拡張プロパティ(Connection.Properties("Extended Properties")= )」から可能でした。しかしODBCのQueryTableでは、Addメソッドの第1パラメータ(Connection)に指定しても機能してくれません。
一方、ListObjectのAddメソッドには「XlListObjectHasHeaders パラメータ(読み込むデータベースにタイトル行が有るか否か)」が設けられていますが、何を指定しても結果は変わらず、機能は無効となっているようです。バージョンで異なるのかもしれませんが、あまり期待しない方が良いかもしれません。
但し、CSVのDSNの中では図09の⑧「列名の見出し」をOFFにすれば、1行目もデータとして読み込んでくれるようです。しかしExcelのDSN(図08)にはそれらしいチェックボックスが見当たらず、「ODBCでは、HDRなどの細かい設定は不可能」と考えた方が良さそうです。

3-2.接続情報の指定方法

QueryTables.AddではConnectionパラメータに、ListObjects.AddではSourceパラメータに「接続情報」を文字列で指定します。今回はODBC接続ですので、「"ODBC;<接続文字列>"」という形で、先頭に「"ODBC"」という文字列を配置し、セミコロン(" ; ")に続けて「ODBCの接続文字列」を指定する事になります。なお、ドライバーやデータベース名などの項目間もセミコロン(" ; ")で区切ります。
なお「"ODBC"」の先頭にスペースを入れて「" ODBC"」としてしまうと、「アプリケーション定義またはオブジェクト定義のエラー」という実行時エラーが発生してしまいます。また、大文字小文字を混在させるのは問題ありませんが、全角を使用するのは当然NGです。
接続情報として必須なのは、図12で示したように「"ODBC"」を先頭とし、「ドライバー名」「データベース名」「DSN名」を組み合わせたものです。以下では図12の3種について、QueryTableを例として紹介します。なお、ListObjectの場合はパラメータ名がSourceに変わるだけで、基本的に同じと考えて良いと思います。

3-2-1.ドライバー名+データベース名を指定

接続情報として「ドライバー名」+「データベース名」を指定する方法(図12の①)が図19です。
  1. '========== ⇩(1) ドライバ名+データベース名の指定方法 ============
  2.  Dim QT As Object    'QueryTableオブジェクト
  3.  Dim ConStr As String    '接続文字列
  4.  ConStr = "ODBC;"
  5.  ConStr = ConStr & "Driver={Microsoft Access Driver (*.mdb, *.accdb)};"
  6.  ConStr = ConStr & "DBQ=" & ThisWorkbook.Path & "¥" & "its-041.accdb;"
  7.  Set QT = Sheet1.QueryTables.Add(Connection:=ConStr, Destination:=Sheet1.Range("B2"))
  8.  ・・・(処理)・・・
図19

04~06行目では、接続文字列(変数ConStr)を作成しています。
まず文字列の先頭にはODBC接続を示すため、04行目「ConStr = "ODBC;"」とします。
05行目「ConStr = ConStr & "Driver={Microsoft Access Driver (*.mdb, *.accdb)};"」では、文字列にドライバー(ここではAccessのドライバー)を追加指定しています。
06行目「ConStr = ConStr & "DBQ=" & ThisWorkbook.Path & "¥" & "its-041.accdb;"」では、文字列にデータベース(ここではAccessのファイル名)を更に追加指定しています。
08行目「Set QT = Sheet1.QueryTables.Add(Connection:=ConStr, Destination:=Sheet1.Range("B2"))」では、QueryTables.Addの第1パラメータ(Connection)に04~06行目で組み立てた接続文字列を指定し、QueryTableオブジェクトを作成しています。

3-2-2.DSN名を指定

接続情報として「DSN名」のみを指定する方法(図12の②)が図20です。
  1. '========== ⇩(2) DSN名の指定方法 ============
  2.  Dim QT As Object    'QueryTableオブジェクト
  3.  Dim ConStr As String    '接続文字列
  4.  ConStr = "ODBC;"
  5.  ConStr = ConStr & "DSN=Ms Access Database;""
  6.  Set QT = Sheet1.QueryTables.Add(Connection:=ConStr, Destination:=Sheet1.Range("B2"))
  7.  ・・・(処理)・・・
図20

まず文字列の先頭にはODBC接続を示すため、14行目「ConStr = "ODBC;"」とします。
15行目「ConStr = ConStr & "DSN=Ms Access Database;"」では、文字列にDSN名(ここではAccessのDSN)を追加指定しています。
17行目「Set QT = Sheet1.QueryTables.Add(Connection:=ConStr, Destination:=Sheet1.Range("B2"))」では、QueryTables.Addの第1パラメータ(Connection)に14~15行目で組み立てた接続文字列を指定し、QueryTableオブジェクトを作成しています。
なお、DSNにデータベースが事前登録されていない場合は、17行目の実行時にデータベースを指定せよ とダイアログが開きます。

3-2-3.DSN名+データベース名を指定

接続情報として「DSN名」+「データベース名」を指定する方法(図12の③)が図21です。
  1. '========== ⇩(3) DSN名+データベース名の指定方法 ============
  2.  Dim QT As Object    'QueryTableオブジェクト
  3.  Dim ConStr As String    '接続文字列
  4.  ConStr = "ODBC;"
  5.  ConStr = ConStr & "DSN=Ms Access Database;""
  6.  ConStr = ConStr & "DBQ=" & ThisWorkbook.Path & "¥" & "its-041.accdb;"
  7.  Set QT = Sheet1.QueryTables.Add(Connection:=ConStr, Destination:=Sheet1.Range("B2"))
  8.  ・・・(処理)・・・
図21

まず文字列の先頭にはODBC接続を示すため、24行目「ConStr = "ODBC;"」とします。
25行目「ConStr = ConStr & "DSN=Ms Access Database;"」では、文字列にDSN名(ここではAccessのDSN)を追加指定しています。
26行目「ConStr = ConStr & "DBQ=" & ThisWorkbook.Path & "¥" & "its-041.accdb;"」では、文字列にデータベース(ここではAccessのファイル名)を更に追加指定しています。
28行目「Set QT = Sheet1.QueryTables.Add(Connection:=ConStr, Destination:=Sheet1.Range("B2"))」では、QueryTables.Addの第1パラメータ(Connection)に24~26行目で組み立てた接続文字列を指定し、QueryTableオブジェクトを作成しています。
なお、DSNにデータベースが事前登録されていても、26行目の「DBQ=・・・」で後から指定するデータベースの方が有効になります。

4.QueryTableを使ったデータベース接続・操作方法

QueryTableを使ってデータベースに接続操作する方法を簡単に示したのが図22です。
QueryTableでの手順概要
図22

まず、QueryTables.AddメソッドでQueryTableオブジェクトを作成します。この作成時には、データ表示位置(ワークシート上のセル位置)を 指定するのと同時に「ODBCの接続情報」を指定します。
出来上がったQueryTableオブジェクトでは、CommandTextプロパティやSQLプロパティに「SQL文」を設定し、最後にRefreshメソッドを実行します。
その結果、設定するSQL文がSelect文の場合は、データ表示位置にデータが出力されます。Select文以外(Insert文・Update文・Delete文)の場合は、データが挿入・変更・削除されます(Select文以外の場合、ワークシート上には「QueryTableオブジェクトの名前」が表示されます)。
なおQueryTableオブジェクトに対してSQL文を設定するのでは無く、QueryTableの作成段階で「QueryTables.Addの第3パラメータとしてSQL文を指定」する手法も可能です。

4-1.オブジェクトの作成

QueryTableオブジェクトを作成するには「QueryTables.Addメソッド」を使用します。構文は以下のようになります。
 QueryTableオブジェクト = シート名.QueryTables.Add (Connection, Destination, Sql)
クエリテーブルは、ワークシート上に作られます。ですので QueryTables.Addメソッドの上流は「クエリテーブルを作るワークシート」となります。このシート名を指定しないとエラーが発生します。
QueryTables.Addメソッドの、3つのパラメータの内容は以下の様になります。
パラメータ内容
Connection必須ODBCの接続文字列を含む文字列 等
Destination必須クエリテーブルの配置先(セル範囲の左上端のセル)
Sql省略可データベースに対し実行されるSQL文
図23

4-1-1.第1パラメータ(Connection:接続情報)

QueryTables.Addでは様々な種類の接続ができるようですが、今回はODBC接続ですので第1パラメータ(Connection)には「"ODBC;<接続文字列>"」という形の接続情報を渡します。接続情報には図11のように複数あり、これらの項目をどのように組み立てて指定するかは「接続文字列について」を参照下さい。

4ー1ー2.第2パラメータ(Destination:配置先)

QueryTables.Addメソッドの第2パラメータには、新規作成するクエリテーブルの配置先をRangeオブジェクトで指定します。このパラメータは必須です。例えば、以下のように第2パラメータを指定します。
「Set QueryTableオブジェクト = Sheet1.QueryTables.Add(Connection:=接続文字列, Destination:=Sheet1.Range("B2"))
このQueryTables.Addメソッドにより作成(オブジェクトのRefreshメソッドの実行後)されるクエリテーブルは、図24のような位置に出力されます。
作成されるクエリテーブルの位置
図24

作成されたQueryTableオブジェクトのプロパティ(FieldNames)により、テーブルのタイトル行が出力されるか否かが変わりますが、Select文を実行した際にはQueryTables.Addメソッドの第2パラメータ(Destination)の指定位置が、タイトル行の有無に関わらず出力範囲の左上セル位置となります。
またSelect文以外(Insert文、Update文、Delete文)の際には、データの出力がありませんので、図24の一番右側のように「クエリテーブルの名前」が第2パラメータで指定したセル位置に表示されます。
またこの時、QueryTables.Addの上流であるシート名「Sheet1」と第2パラメータ(Destination)で指定するセル位置「Sheet1.Range("B2")」は、同じワークシートである必要があります。
 Sheet1.QueryTables.Add(Connection:=接続文字列, Destination:=Sheet1.Range("B2"))」
指定するシートが異なっていると「取り込み先の範囲は、クエリテーブルが作成された同じワークシートにありません」との実行時エラーが発生します。
また、指定するセル範囲は「クエリテーブルの左上の単一セル位置」で充分ですが、例えばSheet1.Range("B2:C3")のように複数セルを指定しても、結局そのセル範囲の左上の単一セルを指定したのと同じ事になります。

4ー1ー3.第3パラメータ(Sql:実行するSQL文)

QueryTables.Addメソッドの第3パラメータは、テーブルに対する処理内容をSQL文として指定します。このパラメータは省略可です。
また、この第3パラメータでSQL文を指定したとしても、作成されたQueryTableオブジェクトのSQLプロパティまたはCommandTextプロパティに再度SQL文を指定することは可能です。その際には「後からプロパティに指定したSQL文が有効」となるようです。
なお、DAOやADOでは可能だった「テーブル名」を指定しての出力や操作は、今回のODBC操作では不可のようです。
また、SQL文内で使用するテーブル名については、「よりみち」のようにExcelやCSVでは特殊な指定の仕方が必要ですので注意して下さい。
寄り道(テーブル名)
各データベースに於ける「テーブル」について図25に示します。
テーブルテーブル名SQLのテーブルとして
Accessテーブル名TestTable"TestTable""Select * from TestTable"
Excelワークシート名Sheet1"Sheet1$""Select * from [Sheet1$]"
CSV等CSVファイル名its-041.csv"its-041.csv""Select * from [its-041.csv]"
図25

Accessに於けるテーブルは、通常のデータベースとしてのテーブルと同じ扱いになります。
Excelでは、ワークシートがテーブル扱いとなります。例えばSheet1にデータがあるとするとテーブル名は「"Sheet1$"」のように「シート名+$マーク」となります。このテーブル名をSQL文の中で使用する場合は、更に角カッコで囲み「Select * from [Sheet1$] 」のようにします。
CSVでは、CSVファイルそのものがテーブル扱いとなります。ファイル名がits-041.csvであればテーブル名はファイル名そのままの「"its-041.csv"」となり、そのテーブル名をSQL文の中で使う場合は角カッコで囲み「Select * from [its-041.csv] 」などとします。

SQL文の構文そのものについては、「データを操作するSQL文」等を参照下さい。

4-2.QueryTableオブジェクトのプロパティとメソッド

QueryTables.Addメソッドで作成されたQueryTableオブジェクトには、多くのプロパティ・メソッドが存在します。今回はデータ作成に必要なメソッドと、出力データに関係する基本的なプロパティ・メソッドのみを説明します。
プロパティ/メソッド内容




CommandText実行するSQL文を指定。
どちらのプロパティに指定してもOK
SQL
CommandType図27参照。ODBCではxlCmdSql(値=2)のみOK
FieldNamesタイトル行の表示有無
True(既定)=表示する、False=表示無し
AdjustColumnWidth列幅の調整
True(既定)=自動調整、False=調整無し
RowNumbers行番号の表示有無
True=表示有り、False(既定)=表示無し



Refresh外部データ範囲の更新
(SQL文の実行)
DeleteQueryTableオブジェクトの削除
図26

4-2-1.CommandText・SQLプロパティ

まず実行するSQL文は、CommandTextプロパティ または SQLプロパティのどちらかに文字列として指定します。どちらのプロパティに指定しても、他方のプロパティに値が転写されます。
なおQueryTables.Addメソッドの第3パラメータにSQL文を指定した場合は、QueryTableオブジェクトの作成直後にはCommandTextプロパティとSQLプロパティの両方に既にSQL文が指定された状態になっています。ですので、その後からCommandTextプロパティ又はSQLプロパティにSQL文を更に指定すると、後から指定したSQL文が有効な状態となります。

4-2-2.CommandTypeプロパティ

CommandTypeプロパティには、CommandTextプロパティ(=SQLプロパティ)に指定したものの種類を、図27内から指定します。なおODBCでは、CommandText・SQLプロパティにはSQL文を指定しますので、CommandTypeプロパティにはxlCmdSql(値=2)を指定します。
なおODBCでは、xlCmdSql(値=2)以外を指定すると「プロシージャの呼び出し、または引数が不正です」とのエラーが発生しますし、ODBCで作成されたQueryTableオブジェクトのCommandTypeプロパティは、既定でxlCmdSql(値=2)となるため、省略してもOKです。
xlCmdType列挙
定数内容
xlCmdCube1OLAPで使用されるキューブ名
xlCmdSql2SQLステートメント(ODBCでは既定)
xlCmdTable3OLEDBでアクセスするテーブル名
xlCmdDefault4OLEDBプロバイダーが認識するコマンドテキスト
xlCmdList5データを一覧表示するためのポインター
xlCmdTableCollection6テーブルコレクションの名前
xlCmdExcel7Excelの式
xlCmdDAX8DAX(Data Analysis Expressions)の式
図27

4-2-3.FieldNamesプロパティ

FieldNamesプロパティは、Select文を指定した時の出力されるデータの「タイトル行を表示するか否か」です。True(既定)を指定するとタイトル行が表示され、Falseを指定すると表示されません。
図24では、タイトル行表示が一番左側の図で、表示無しが中央の図となります。

4-2-4.AdjustColumnWidthプロパティ

AdjustColumnWidthプロパティは、Select文を指定した時の出力されるセル範囲の「データ表示幅を自動的に調整するか否か」です。図28のようにTrue(既定)を指定すると列幅は自動調整され、Falseでは調整されずにそのままの列幅となります。
AdjustColumnWidthプロパティ値による表示の違い
図28

図28の左図でも分かるように、データの文字数が多い場合は文字列が切れてしまったり、桁数が多い数値の場合はハッシュ印(#)になってしまうので注意が必要です。

4-2-5.RowNumbersプロパティ

RowNumbersプロパティは、出力されるデータの行番号を表示するか否かです。図29のように、既定のFalseは行番号の表示無しで、Trueが行番号表示となります。行番号はゼロ始まりのようです。
RowNumbersプロパティ値による表示の違い
図29

行番号を追加することで出力表の全体が右側に移動する形ですが、注目したいのはD列の表示形式です。データベース上では「Nu列=数値」「DT列=日付」なのですが、行番号非表示(図29の左側)の時は正しく表示されますが、行番号表示(図29の右側)の時は正しく表示されません。
図29の左右図を見比べてみると「貼り付け先セル範囲を行番号非表示のデータ並びで書式変更し、その後で行番号有りのデータを張り付け」ている感じです(全ての書式を変更する訳ではなさそうです。標準書式で正しく表示できる列はそのままで、日付のような書式だけを都度設定するようです)。
セルから値を取り込む際にデータ変換等を行えば問題ないとは思いますが、そもそも行番号も含めて出力する必要性はあまり高く無いかもしれません。

4-2-6.Refreshメソッド

Refreshメソッドは、QueryTables.Addで接続したデータベースに対し、CommandText又はSQLプロパティに指定したSQL文を実行し、QueryTableのデータを更新します。つまりAddメソッドの第3パラメータにSQL文を指定しても、Addの実行のみではデータは出力されず、Refreshメソッドを実行して初めて出力(Select文以外では、データの追加・変更・削除)されます。
Refreshメソッドには省略可能なパラメータ(BackgroundQuery)があります。「True」または「False」を指定し、既定はODBCの場合はTrueのようです。
Trueの場合、RefreshメソッドでSQL文を実行すると、直ちに次のコードに制御が移ります。一方Falseを指定した場合は、SQL文を実行しワークシートに出力が全て完了してから次のコードに移ります。
ですので、Refreshメソッドのすぐ後で「ワークシート上に出力されたデータを処理」する場合は「BackgroundQuery:=False」のパラメータを指定する必要があります(Trueを指定すると最悪の場合、中途半端な出力データを処理する事になる)。
但し「BackgroundQuery:=False」をSelect文以外(Insert、Update、Delete)の時に指定すると、「アプリケーション定義またはオブジェクト定義のエラー」という実行時エラーが発生します。しかしエラー発生の直前にSQL文は実行されてしまうようで、データベースへの挿入・変更・削除は完了している、というちょっと困った状態となります。ですので「Select文以外ではTrue指定またはパラメータを省略」する必要があります。

4-2-7.Deleteメソッド

Deleteメソッドは、文字通りQueryTableオブジェクトを削除します。
なおDeleteメソッドはオブジェクトは削除しますが、ワークシート上に出力済みのデータ(文字列や数値)は削除しません。ですのでセルの値も削除する際には、「QueryTable.ResultRange(QueryTableが占めるセル領域)」や「QueryTable.Destination.CurrentRegion(QueryTableの左上セルを含むセル範囲)」を使ってQueryTableの範囲を取得した上で、ClearやClearContentsメソッドでデータを削除します。
なおMicrosoftのサイトでは「ResultRangeの範囲には、フィールド名の行または行番号の列は含まれない」との記述があります。しかし試してみると、今回のようなQueryTables.Add + Refreshメソッドで出力されたクエリテーブルでは「フィールド名の行と行番号は、ResultRangeの範囲内に含まれる」結果となりました。これはバグか否かは分かりません。
一方Destinationは、QueryTables.Addメソッドの第2パラメータに指定した場所のはずですので、そこを起点にしたCurrentRegionプロパティでの範囲は、確実なデータ範囲と考えることができます。

4ー3.SQL文の指定方法

図12」や「接続情報の指定方法」で説明した通り、データベースへの接続方法は3通りが考えられます。またQueryTableではSQL文の指定方法も複数通りが考えられます。
ここでは接続情報の指定方法を「ドライバー(Driver=)」+「データベース(DBQ=)」に固定し、SQL文をどこに指定するかについて説明していきます。

4ー3-1.QueryTables.Addメソッドの第3パラメータに指定

まずSQL文をQueryTables.Addメソッドの第3パラメータに指定するのが図30です。
  1. '========== ⇩(4) QueryTables.Addメソッドの第3パラメータに指定 ============
  2.  Dim ConStr As String    '接続文字列
  3.  Dim SqlStr As String    '実行するSQL文
  4.  ConStr = "ODBC;"
  5.  ConStr = ConStr & "Driver={Microsoft Access Driver (*.mdb, *.accdb)};"
  6.  ConStr = ConStr & "DBQ=" & ThisWorkbook.Path & "¥" & "its-041.accdb;"
  7.  SqlStr = "select * from TestTable"
  8.  With Sheet1.QueryTables.Add(Connection:=ConStr, Destination:=Sheet1.Range("B2"), SQL:=SqlStr)
  9.   .Refresh BackgroundQuery:=False
  10.  End With
図30

48行目「SqlStr = "select * from TestTable"」では、SQL文を作成しています。
50行目「With Sheet1.QueryTables.Add(Connection:=ConStr, Destination:=Sheet1.Range("B2"), SQL:=SqlStr)」では、Sheet1上にQueryTableを作成しています。
第1パラメータ「Connection:=ConStr」では、44~46行目で作成した「ODBC」+「ドライバー」+「データベース」の文字列を指定することで、AccessのODBCドライバーとAccessデータベースファイルに接続させています。
第2パラメータ「Destination:=Sheet1.Range("B2")」は、出力データの書き込みセル範囲(範囲の左上セル位置)の指定をしています。
第3パラメータ「SQL:=SqlStr」では、48行目で作成したSQL文を実行SQL文として指定しています。
51行目「.Refresh BackgroundQuery:=False」でSQL文が実行され、ワークシート上にデータが出力されます。

4ー3-2.QueryTableオブジェクトのCommandTextプロパティに指定

次に、SQL文はQueryTables.Addの第3パラメータにでは無く、QueryTableオブジェクトのCommandTextプロパティに指定します。
  1. '========== ⇩(5) QueryTableオブジェクトのCommandTextプロパティに指定 ============
  2.  Dim ConStr As String    '接続文字列
  3.  Dim SqlStr As String    '実行するSQL文
  4.  ConStr = "ODBC;"
  5.  ConStr = ConStr & "Driver={Microsoft Access Driver (*.mdb, *.accdb)};"
  6.  ConStr = ConStr & "DBQ=" & ThisWorkbook.Path & "¥" & "its-041.accdb;"
  7.  SqlStr = "select * from TestTable"
  8.  With Sheet1.QueryTables.Add(Connection:=ConStr, Destination:=Sheet1.Range("B2"))
  9.   .CommandText = SqlStr
  10.   .Refresh BackgroundQuery:=False
  11.  End With
図31

68行目「SqlStr = "select * from TestTable"」でSQL文を作成します。
70行目「With Sheet1.QueryTables.Add(Connection:=ConStr, Destination:=Sheet1.Range("B2"))」では、Sheet1上にQueryTableを作成していますが、第3パラメータ(SQL)は省略しています。
作成したQueryTableオブジェクトに対し、71行目「.CommandText = SqlStr」ではCommandTextプロパティに68行目で作成したSQL文を指定しています。
72行目「.Refresh BackgroundQuery:=False」でSQL文が実行され、ワークシート上にデータが出力されます。

4ー3-3.QueryTableオブジェクトのSQLプロパティに指定

最後に、SQL文をQueryTableオブジェクトのSQLプロパティに指定しています。なお、QueryTableオブジェクトをいったん変数QTとして取り出し、そのオブジェクト変数に対して処理を行っています。
  1. '========== ⇩(6) QueryTableオブジェクトのSQLプロパティに指定 ============
  2.  Dim QT As Object    'QueryTableオブジェクト
  3.  Dim ConStr As String    '接続文字列
  4.  Dim SqlStr As String    '実行するSQL文
  5.  ConStr = "ODBC;"
  6.  ConStr = ConStr & "Driver={Microsoft Access Driver (*.mdb, *.accdb)};"
  7.  ConStr = ConStr & "DBQ=" & ThisWorkbook.Path & "¥" & "its-041.accdb;"
  8.  SqlStr = "select * from TestTable"
  9.  Set QT = Sheet1.QueryTables.Add(Connection:=ConStr, Destination:=Sheet1.Range("B2"))
  10.  With QT
  11.   .SQL = SqlStr
  12.   .Refresh BackgroundQuery:=False
  13.  End With
  14.  Set QT = Nothing
図32

89行目「SqlStr = "select * from TestTable"」で、SQL文を作成します。
91行目「Set QT = Sheet1.QueryTables.Add(Connection:=ConStr, Destination:=Sheet1.Range("B2"))」では、Sheet1上にQueryTableを作成していますが、第3パラメータ(SQL)は省略しています。
作成したQueryTableオブジェクト(変数QT)に対し、94行目「.SQL = SqlStr」でSQLプロパティに89行目で作成したSQL文を指定しています。
95行目「.Refresh BackgroundQuery:=False」でSQL文が実行され、ワークシート上にデータが出力されます。
先頭の81行目では、変数QTを「Dim QT As Object」と宣言しています。データ型としてObject型の代わりにQueryTable型を使用し「Dim QT As QueryTable」と宣言してもOKです。

4ー4.ワークシート上のQueryTable等の削除

上記のようなクエリテーブル出力のコードを実行すると、図33の左側のように表示されます。しかしクエリテーブルを削除せずに、更にクエリテーブルを出力させると図33の右側のように、横に2つのテーブルが並んだような状態になってしまいます。
動きとしては、古いテーブルの右端に新たなテーブルが割り込み「古いテーブルより右側全体」が右側に移動するようです。
前回のQueryTableが残っている状態で更にQueryTableを作成した状態
図33

複数のクエリテーブルが並んでいては処理が面倒になりますので「古いテーブルは削除してから、新たなテーブルを作る」のが良いと思います。図34は、テーブル類を削除するコード例です。
  1. '========== ⇩(7) ワークシート上の既存の表を削除 ============
  2. Sub ListQuery_Delete()
  3.  With Sheet1
  4.   If Not .QueryTables.Count = 0 Then
  5.    .QueryTables(1).Destination.CurrentRegion.Clear
  6.    .QueryTables(1).Delete
  7.   End If
  8.   If Not .ListObjects.Count = 0 Then
  9.    .ListObjects(1).Delete
  10.   End If
  11.  End With
  12. End Sub
図34

113行目「With Sheet1」では、処理するテーブルが存在するワークシートを指定しています。
114行目「If Not .QueryTables.Count = 0 Then」は、そのシート上にクエリテーブルがいくつ存在するかを確認しています。ゼロ個では無い(=存在している)場合に115~116行目を実行します。
115行目「.QueryTables(1).Destination.CurrentRegion.Clear」では、クエリテーブルの左上セル(=QueryTables.Addの第2パラメータに指定したセル位置)を起点に、データがつながっているセル範囲(CurrentRegion)を取得し、そのセル範囲の「データ」を削除しています。
116行目「.QueryTables(1).Delete」では、クエリテーブル自体を削除しています。なおクエリテーブルをDeleteしただけでは、テーブルは削除されますがセル上のデータは残ったままになってしまいます。そのため、Deleteの前に115行目でデータを削除しています。
114~117行目で「クエリテーブルの削除」については充分ですが、「ListObjectを使ったデータベース接続・操作」ではデータを同じワークシート上のListObjectに出力しています。ですので一緒にListObjectを削除するコードも盛り込んでいます。
119行目「If Not .ListObjects.Count = 0 Then」で、そのシート上にListObjectがいくつ存在するかを確認しています。ゼロ個では無い(=存在している)場合に120行目を実行します。
120行目「.ListObjects(1).Delete」ではListObjectオブジェクトを削除しています。
図34は「ワークシート上には、クエリテーブル及びListObjectが1つしか無い」ことが前提です。複数テーブルをまとめて削除する必要があったり、また複数テーブルを駆使するようなアプリの場合には更に工夫が必要です。
また図30図31図32では、この削除処理を盛り込んでいないコードにしていますが、このコードをQueryTables.Addの前に行うことで、後の処理が少し簡単になっていくと思います。

4ー5.データベース種と操作種の違いによる操作方法

ここまでは接続するデータベースをAccessに固定し、SQL文もSelect文のみでした。しかしQueryTableを使ったデータベース接続ではAccess以外のExcelやCSVにも接続可能ですし、またInsert文・Update文・Delete文も実行可能です。
そこで図37では、様々なデータベース、および様々なSQL文に対応したコードを紹介します。
なお、ここではデータベース接続には「ドライバー名」+「データベース名」を指定し、SQL文はQueryTableのCommandTextプロパティに指定することとしますが、「接続情報の指定方法」「SQL文の指定方法」で説明した通り、別な方法を使ってもOKです。
ここで扱うデータは、全て以下のような形の「4列×4行」としています。St、Nu、Dt、BLが列名になります。Excel、CSVも先頭行は列名行としています。
St(String型)Nu(Double型)Dt(Date型)BL(Boolean型)
"abc"1232023/1/2True
"def"1232023/2/2False
"abc"4562023/3/2False
"def"4562023/4/2True
図35

なお、Accessのデータファイル(its-041.accdb:添付ファイル)の作成法については「DAOでAccess・Excel・CSV等に接続し簡易操作」を参照下さい。そのAccessのデータを置いてあるテーブル名は「TestTable」としています。
またCSVは図35のデータを手作業で入力したCSVファイル(its-041.csv:添付ファイル)を使用し、Excelは添付ファイルの「シート名:DB」にデータを置いています。
また、図37ではExcelのシートデータベースについて説明していきますが、以下のようにコードを切り替えることで、データベースをAccess・CSVに切り替えることが出来ます。
ドライバ(Driver=)データベース(DBQ=)SQL文内
のテーブル
SQL文
Access
{Microsoft Access Driver (*.mdb, *.accdb)}
④パス名+ファイル名
ThisWorkbook.Path & "¥" & "its-041.accdb"
⑦テーブル名
TestTable
⑩ Select文
⑪ Insert文
⑫ Update文
⑬ Delete文
Excel
{Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)}
⑤パス名+ファイル名
ThisWorkbook.Path & "¥" & ThisWorkbook.Name
⑧シート名+$印
[db$]
⑩ Select文
⑪ Insert文
⑫ Update文
CSV
{Microsoft Access Text Driver (*.txt, *.csv)}
⑥パス名
ThisWorkbook.Path & "¥"
⑨CSVファイル名
[its-041.csv]
⑩ Select文
⑪ Insert文
図36

なお図36のSQL文の列でもわかるように、Accessは全ての操作(Select・Insert・Update・Delete)が可能ですが、ExcelはDelete操作は不可(Select・Insert・Update操作のみ可)、CSVはUpdate・Delete操作が不可(Select・Insert操作のみ可)となります。
  1. '========== ⇩(8) 各データベースへの接続・操作 ============
  2. Sub ODBC_QueryTable01()
  3.  Dim QT As Object   '←QueryTableオブジェクト
  4.  Dim ConStr As String   '←接続文字列
  5.  Dim SqlStr As String   '←'実行するSQL文
  6.  Dim toNextCode As Boolean   '←次のコードに進むか否か
  7.  Call ListQuery_Delete   '←既存のテーブルを削除
  8.  ConStr = "ODBC;"
  9. ' ConStr = ConStr & "Driver={Microsoft Access Driver (*.mdb, *.accdb)};"   '←①Accessドライバー情報
  10. ' ConStr = ConStr & "DBQ=" & ThisWorkbook.Path & "¥" & "its-041.accdb;"   '←④Accessデータベース情報
  11.  ConStr = ConStr & "Driver={Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)};"   '←②Excelドライバー情報
  12.  ConStr = ConStr & "DBQ=" & ThisWorkbook.Path & "¥" & ThisWorkbook.Name & ";"   '←⑤Excelデータベース情報
  13. ' ConStr = ConStr & "Driver={Microsoft Access Text Driver (*.txt, *.csv)};"   '←③CSVドライバー情報
  14. ' ConStr = ConStr & "DBQ=" & ThisWorkbook.Path & "¥;"     '←⑥CSVデータベース情報
  15.  ConStr = ConStr & "ReadOnly=0;"
  16. ' SqlStr = "TestTable"   '←⑦Accessテーブル
  17.  SqlStr = "[db$]"      '←⑧Excelテーブル(シート名)
  18. ' SqlStr = "[its-041.csv]"   '←⑨CSVテーブル(ファイル名)
  19.  SqlStr = "select * from " & SqlStr   '←⑩Select文
  20. ' SqlStr = "insert into " & SqlStr & " (st,nu,dt,bl) values ('PQR',897,#11/20/2000#,True)"   '←⑪Inset文
  21. ' SqlStr = "Update " & SqlStr & " set St='XYZ' where DT=#2000/11/20#"     '←⑫Update文
  22. ' SqlStr = "Delete from " & SqlStr & " where Dt=#11/20/2000#"     '←⑬Delete文
  23.  toNextCode = Not (UCase(Left(Trim(SqlStr), 1)) = "S")
  24.  Set QT = Sheet1.QueryTables.Add(Connection:=ConStr, Destination:=Sheet1.Range("B2"))
  25.  With QT
  26. '  .CommandType = 2      '←CommandTextの種類。ODBCではxlCmdSql(値=2)が既定
  27.   .CommandText = SqlStr    '←実行するSQL文
  28. '  .FieldNames = True      '←タイトル行の表示有無。True(既定)=表示有り
  29. '  .RowNumbers = False     '←行番号の表示有無。False(既定)=表示無し
  30. '  .AdjustColumnWidth = True   '←列幅の調整。True(既定)=自動調整
  31.   .Refresh BackgroundQuery:=toNextCode
  32.  End With
  33.  Set QT = Nothing
  34. End Sub
図37

137行目「Call ListQuery_Delete」は図34のプロシージャを呼び出し、「ワークシート上の既存のテーブルを削除」しています。これから作成するテーブルのみがワークシート上にある状態にするためです。
139~149行目では、データベースに接続するための接続文字列(変数ConStr)を組み立てています。
まず文字列の先頭にはODBC接続を示すため、139行目「ConStr = "ODBC;"」とします。
143行目「ConStr = ConStr & "Driver={Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)};"」では、文字列にドライバー名を追加指定しています。
144行目「ConStr = ConStr & "DBQ=" & ThisWorkbook.Path & "¥" & ThisWorkbook.Name & ";"」では文字列にデータベース名を更に追加指定しています。 なお、Accessに接続する場合は140~141行目を、CSVの場合は146~147行目のドライバーとデータベース名を使用します。
149行目「ConStr = ConStr & "ReadOnly=0;"」では、読取/書込の条件として「ReadOnly=0」を指定し、書き込み可能状態にしています。図18の通り、このReadOnlyを省略してしまうとExcelでは読取専用となってしまうので、Insert文などを実行するときには必須となります。
なお、160行目の「SQL文の種類抽出式」から「ReadOnly=0とReadOnly=1の切り替え」を行うことは可能ですが、今回のテーマとは外れるので省略しました。
151~158行目では、処理を行うSQL文(変数SqlStr)を組み立てています。
今回はExcelのデータベースに接続しますので、まずテーブル名を変数SqlStrに格納するため152行目「SqlStr = "[db$]"」でExcelのワークシート名を指定します。この時、シート名には「$印」を付けた上で全体を角カッコで囲みます。この指定方法については、図25を参照下さい。また、Accessの場合は151行目、CSVの場合は153行目のようになります。
155行目「SqlStr = "select * from " & SqlStr」では、Select文を組み立てています。この時のテーブル名は152行目で準備した文字列を使用します。テーブル名とFrom句の間にスペースを入れるのを忘れないようにして下さい。
Select文以外は156~158行目のInsert文・Update文・Delete文を参照下さい。
160行目「toNextCode = Not (UCase(Left(Trim(SqlStr), 1)) = "S") 」は、「Select文の時にFalse」「Select文以外の時はTrue」とするコードです。この後170行目のRefreshメソッドでSQL文を実行させますが、Select文の場合はすぐに次のコードに制御が移ってしまうのを防止するために「BackgroundQuery:=False」のパラメータが必要ですし、Select文以外の場合は「BackgroundQuery:=True(または省略)」が必要です。
その分岐を、今回はSQL文の先頭文字を使った数式で表してみました。
つまり今回使用するSQL文の先頭ワードは、Select・Insert・Update・Deleteですので、その先頭文字列を切り出し「S」か否かを調べています。Sであればカッコ内はTrueですが、Notで反転させていますのでFalseとなります。なお先頭にスペースが入っている場合が考えられるのでTrim関数で取り除き、UCaseで大文字に揃えています。
162行目「Set QT = Sheet1.QueryTables.Add(Connection:=ConStr, Destination:=Sheet1.Range("B2")) 」では、139~149行目で組み立てた接続文字列を使い、QueryTableオブジェクトを作成しています。その際出力先(左上セル)はB2セルにするように第2パラメータ(Destination)に指定しています。
164行目「With QT」では、162行目で作成したQueryTableオブジェクト(オブジェクト変数QT)に対し、165~170行目の処理をしていきます。
166行目「.CommandText = SqlStr」では、CommandTextプロパティに151~158行目で組み立てたSQL文を指定しています。なおこのSQL文をQueryTableオブジェクトのSQLプロパティに指定しても同じです。
その他(CommandType、FieldNames、RowNumbers、AdjustColumnWidth)のプロパティの指定は見え消しにしてあり全て既定値となりますが、必要に応じて変更が可能です。
170行目「.Refresh BackgroundQuery:=toNextCode」で、Refreshメソッドにより、SQL文の実行をします。RefreshメソッドにはBackgroundQueryパラメータがありますが、Select文の場合は全データを出力後に次のコードに移動させるために「BackgroundQuery:=False」を指定する必要があります。またSelect文以外では「BackgroundQuery:=True」を指定(または省略)しないと、エラーが出てしまいます。
そのために、SQL文の先頭文字列を使ってSelect文(False)、Select以外(True)を160行目のコードで作り、BackgroundQueryパラメータに指定しています。
なお図37のコードを単一で実行する限りは、Refreshメソッドのパラメータ(BackgroundQuery)にどちらを指定しても、出力が途中で途切れることはありません。そのままプロシージャが完了してしまうためです。しかし通常はその出力データに対しての処理を行うことになると思いますので、欠落したデータを処理しないためにも、「Select文には、BackgroundQuery:=False」のパラメータ設定が必要と思います。

5.ListObjectを使ったデータベース接続・操作方法

ListObjectを使ってデータベースに接続操作する方法を簡単に示したのが図38です。
ListObjectでデータベースに接続・操作する方法
図38

まず、ListObjects.AddメソッドでListObjectオブジェクトを作成します。この作成時には、データ表示位置(ワークシート上のセル位置)を指定するのと同時に「ODBCの接続情報」などを指定します。 出来上がったListObjectオブジェクトのQueryTable.CommandTextプロパティやQueryTable.SQLプロパティに「SQL文」を設定し、最後にRefreshメソッドを実行します。 その結果、設定するSQL文がSelect文の場合は、データ表示位置にデータが出力されます。Select文以外(Insert文・Update文・Delete文)の場合は、データが挿入・変更・削除されます(Select文以外の場合、ワークシート上には「ListObjectオブジェクトの名前」が表示されます)。
なおQueryTableオブジェクトの場合と酷似していますが、作られるオブジェクトがListObjectであることから、SQL文を設定するプロパティがListObjectオブジェクトの直下にはありません。また、ListObjects.AddのパラメータにSQL文を格納するパラメータが無いことなどが異なります。

5-1.オブジェクトの作成

ListObjectオブジェクトを作成するには「ListObjects.Addメソッド」を使用します。構文は以下のようになります。
 ListObjectオブジェクト = 
   シート名.ListObjects.Add (SourceType, Source, LinkSource, XlListObjectHasHeaders, Destination, TableStyleName)
リストオブジェクトは、ワークシート上に作られます。ですので ListObjects.Addメソッドの上流は「リストオブジェクトを作るワークシート」となります。このシート名を指定しないとエラーが発生します。
ListObjects.Addメソッドの、6つのパラメータの内容は以下の様になります。
ListObjects.Addメソッドのパラメータ
パラメータ内容
SourceTypeODBCは必須Sourceの種類(図40)
SourceODBCは必須ODBC接続文字列
LinkSource省略可外部データソースをListObjectにリンクするか否か
XlListObjectHasHeaders省略可先頭行が見出しか否か
DestinationODBCは必須データ出力先の左上セル
TableStyleName省略可テーブルスタイルの名前
図39

ListObjects.Addのパラメータは基本は全て省略可なのですが、ODBCで接続するに当たっては、第1(SourceType)、第2(Source)、第5(Destination)パラメータは必須となります。

5-1-1.第1パラメータ(SourceType:元データの種類)

第1パラメータ(SourceType)には、第2パラメータ(Source)に指定する元データの種類を指定します。
SourceType
定数内容
xlSrcExternal0外部データソース
xlSrcRange1Range範囲(既定)
xlSrcXml2XML
xlSrcQuery3クエリ
xlSrcModel4PowerPivotモデル
図40

今回は、第2パラメータ(Source)にはODBCの接続情報を指定します。そしてODBCの先にあるのは、AccessやExcel、CSVといった「SQL文でデータを操作するデータベース」ですので、xlSrcQuery(値=3)選択します。なおxlSrcExternal(値=0)を指定してもOKです。それ以外を指定(または省略)すると「プロシージャの呼び出し、または引数が不正です」との実行時エラーが発生します。

5-1-2.第2パラメータ(Source:元データの種類)

今回はODBC接続をしますので、第2パラメータ(Source)には「"ODBC;<接続文字列>"」という形の接続情報を渡します。接続情報には図11のように複数あり、これらの項目をどのように組み立てて指定するかは「接続文字列について」を参照下さい。

5-1-3.第3パラメータ(LinkSource:元データとリンクするか否か)

第3パラメータ(LinkSource)は、元データとワークシート上に作成したテーブルの間をリンクで結ぶか否か(True または False)の設定です。リンクしておく(True設定)と自動的にデータが更新されるとの記述もあります。第1パラメータがxlSrcExternal(値=0)の場合の既定値はTrueとのことです。
しかし元データを変更してもテーブルのデータが自動的に更新されることはありません(ワークシートの再計算(F9キー)をしても更新されない)。テーブルのデータを更新するには、Refreshメソッドの実行や、Excelの「データ」タブ→「クエリと接続」→「更新」ボタンをクリックすることが必要で、その操作によりもちろんデータ更新はされます。しかし、この機能はLinkSourceパラメータをどちらの設定にしても同じです。
つまり、少なくとも「ODBC接続でのデータベース~作成されたテーブル間」では、LinkSourceパラメータの設定は無効と言うか、設定の意味がありません(省略でOK)。

5-1-4.第4パラメータ(XlListObjectHasHeaders:元データのタイトル行の有無)

第4パラメータ(XlListObjectHasHeaders)は、元データにタイトル行があるか否かの設定です。設定値は図41の値です。
XlYesNoGuess列挙
定数内容
xlGuesss0自動検出(既定?)
xlYes1タイトル行有り
xlNo2タイトル行無し(全てデータ)
図41

しかしODBCの場合、どの値を指定しても「データの先頭行=タイトル行」と判断するようなので、設定する意味がありません(≒省略でOK)。逆に、タイトル行の無いデータでは「先頭データ=タイトル」となるので注意が必要です。

5-1-5.第5パラメータ(Destination:配置先)

第5パラメータ(Destination)には、新規作成するテーブルの配置先をRangeオブジェクトで指定します。このパラメータはODBC接続では必須です。例えば、以下のように指定します。
「Set ListObjectオブジェクト = Sheet1.ListObjects.Add(SourceType:=xlSrcQuery, Source:=接続文字列, Destination:=Sheet1.Range("B2"))
このListObjects.Addメソッドにより作成(オブジェクトのRefreshメソッドの実行後)されるテーブルは、図24のような位置に出力されます。
作成されるクエリテーブルの位置
図42

Select文を実行した際には、作成されるListObjectオブジェクトのプロパティ(ShowHeaders)により、テーブルのタイトル行が出力されるか否かが変わります。
タイトル行あり(既定のTrue)の場合は、図42の左側のように「Destinationパラメータで指定した位置」が、タイトル+データの起点となります。一方、タイトル行無し(False)の場合は図42の中央のように、Destinationパラメータで指定した位置の「1つ下のセル」を起点とした位置からデータが始まります。
またSelect文以外(Insert文、Update文、Delete文)の際には、データの出力がありませんので図42の右側上のように「ListObjectオブジェクトの名前」+「データの取り出し中...」のような文字が指定セル位置(タイトル部)に表示されます。
但しタイトルなしの設定の場合は、図42の右側下のように「何も表示されない(セル背景色は変わる)」状況になります。
QueryTableオブジェクトの場合(図24)は、どんな場合でも「Destinationパラメータで指定した位置」から始まりましたが、ListObject(図42)では、タイトル行があると仮定して「Destinationパラメータで指定した位置=タイトルの始点」となるようです。
またこの時、ListObjects.Addの上流であるシート名「Sheet1」と第5パラメータ(Destination)で指定するセル位置は、同じワークシートである必要があります。
 Sheet1.ListObjects.Add(SourceType:=xlSrcQuery, Source:=接続文字列, Destination:=Sheet1.Range("B2"))」
指定するシートが異なっていると「テーブルのワークシートデータはテーブルと同じシート上になければなりません」との実行時エラーが発生します。
また、指定するセル範囲は「テーブルの左上の単一セル位置」である必要があります。QueryTabelオブジェクトでは許されていた複数セルの指定をすると「プロシージャの呼び出し、または引数が不正です」という実行時エラーが発生します。

5-1-6.第6パラメータ(TableStyleName:テーブルスタイル名)

通常、第6パラメータ(TableStyleName)には、作成するListObject(テーブル)のスタイルを文字列で指定するのですが、ODBCでデータベース接続する際は、何を指定しても無効となるようです。
もしテーブルのスタイルを変更したい場合は、テーブル作成後にListObjectオブジェクトのTableStyleプロパティにスタイル名を指定します。詳細は「TableStyleプロパティ」を参照下さい。

5-2.ListObjectオブジェクトのプロパティとメソッド

ListObjects.Addメソッドで作成されたListObjectオブジェクトには、多くのプロパティ・メソッドが存在します。今回はデータ作成等に必要なメソッドと、出力テーブルに関係するプロパティのみを紹介します。
なおListObjectオブジェクト直下のプロパティ・メソッドと合わせ、その下のQueryTableオブジェクト配下のプロパティ・メソッドも存在します。
プロパティ/メソッド内容




ShowHeadersシート出力するデータのタイトル行の有無
True(既定)=表示/False(非表示)
ShowAutoFilterフィルターボタンの表示有無
True(既定)=表示/False(非表示)
TableStyleテーブルのスタイル(スタイル名を文字列で指定)
QueryTableCommandText実行するSQL文を指定。
どちらのプロパティに指定してもOK
SQL
CommandTypeCommandText、SQLプロパティに指定したデータ種
AdjustColumnWidth列幅の調整
True(既定)=自動調整、False=調整無し
RowNumbers行番号の表示有無
True=表示有り、False(既定)=表示無し



RefreshSQL文を実行。Select文の時はテーブルにデータを出力
DeleteListObjectオブジェクトの削除
(テーブルやデータも削除される)
QueryTableRefreshListObject.Refreshと同等か?
DeleteQueryTableオブジェクトの削除
(テーブルやデータは削除されない)
図43

5-2-1.ShowHeadersプロパティ

ListObjectオブジェクトのShowHeadersプロパティは、ワークシート上に出力するテーブルのタイトル行の表示/非表示を指定します。True(既定)は表示をし、Falseは非表示です。
表示/非表示設定では、図42の左図/中央図のような形になり、非表示設定ではDestinationパラメータで指定したセル位置とは異なる位置に出力される形になるので、注意が必要です。
Select文以外では図42の右上図/右下図のようになり、タイトル行が非表示だと「取り出し中・・」の文字列すら出なくなるので、処理時に注意が必要です。

5-2-2.ShowAutoFilterプロパティ

ListObjectオブジェクトのShowAutoFilterプロパティは、ワークシート上に出力するテーブルのタイトル行の「フィルターボタン」の表示/非表示を指定します。True(既定)は表示をし、Falseは非表示です。
表示有無の様子は、図44のようになります。なおフィルターボタン有無で、ボタン幅の分だけ列幅が変わるようです。
フィルターボタンの有無
図44

5-2-3.TableStyleプロパティ

ListObjectオブジェクトのTableStyleプロパティは、作成したテーブルのスタイルを文字列で指定します。既定値は「"TableStyleMedium2"」となっているようです。
テーブルのスタイルは、リボンの「テーブルデザイン」タブ→「テーブルスタイル」グループ→「クイックスタイル」で表示されるダイアログ内から手動で選べます。一方VBAでは、そのスタイル形式の「文字列」を指定することになります。形式は図45のように「(淡色)TableStyleLight」「(中間)TableStyleMedium」「(濃色)TableStyleDark」の後に数字をつけた文字列です。なお自作もできるようです。
TableStyle
図45

例えば、「中間」+「左上から2番目」のスタイルは「"TableStyleMedium2"」というスタイル形式になります。
なお「""(長さゼロの文字列)」を指定すると、図45の左図の左上角の「書式なし」の状態になります。

5-2-4.CommandText・SQLプロパティ

CommandTextプロパティまたはSQLプロパティには、データベースに対するSQL文を指定します。どちらのプロパティに指定しても、他方のプロパティに値が転写されます。
この2つのプロパティはListObjectオブジェクト直下では無く、ListObjectの中のQueryTableオブジェクトの下にあります。

5-2-5.CommandTypeプロパティ

CommandTypeプロパティには、CommandTextプロパティ(=SQLプロパティ)に指定したものの種類を、図27内から指定します。このプロパティも、ListObjectの中のQueryTableオブジェクトの下にあります。
なおODBC接続では、CommandText・SQLプロパティにはSQL文を指定しますので、CommandTypeプロパティにはxlCmdSql(値=2)を指定します。
なおODBC接続では、xlCmdSql(値=2)以外を指定すると「プロシージャの呼び出し、または引数が不正です」とのエラーが発生しますし、ODBCで作成されたListObject内のQueryTable.CommandTypeプロパティは、既定でxlCmdSql(値=2)となるため、省略してもOKです。

5-2-6.AdjustColumnWidthプロパティ

AdjustColumnWidthプロパティは、Select文を指定した時の出力されるセル範囲のデータ表示幅を「自動的に調整するか否か」です。このプロパティも、ListObjectの中のQueryTableオブジェクトの下にあります。
AdjustColumnWidthプロパティ値による表示の違い
図46

True(既定)を指定すると、図46の左図のように列幅は自動調整されます。一方Falseを指定した場合は、ListObjectをAdd(作成)した時点(図46の中央図)で「テーブル名」+「データの取り出し中・・・」の文字列が出る事により先頭列の列幅が広くなり、そのあと列幅が非自動であるため、その幅広のままの状態でデータが出力(図46の右図)されることになります。
なおデータの文字数が多い場合は文字列が切れてしまったり、桁数が多い数値の場合はハッシュ印(#)になってしまうので注意が必要です。また、例えタイトル行を非表示に設定しても、Add(ListObjectを作成)時にはタイトル行は存在しますので、先頭列の幅広は変わりません。

5-2-7.RowNumbersプロパティ

RowNumbersプロパティは、出力されるデータの行番号を表示するか否かです。このプロパティも、ListObjectの中のQueryTableオブジェクトの下にあります。
図47のように、Trueを指定すると行番号表示となります。行番号はゼロ始まりのようです。なお既定はFalse(行番号を非表示)です。
RowNumbersプロパティ値による表示の違い
図47

QueryTableオブジェクト(図29)の時には、D列の表示形式がDt列(日付型)の影響を受けて数値→日付型で表示されていましたが、ListObjectでは正常に表示されるようです。
但し、ListObjectでは日付型は数値として表示(図47のE列)されてしまうようなので、シート上に表示されたデータを目視で使用する場合にはデータ型変換などの工夫が必要となります。

5-2-8.Refreshメソッド

Refreshメソッドは、ListObjectオブジェクト直下と合わせ、QueryTableオブジェクトにも存在するようです。
機能的には2つとも同じみたいで、CommandText又はSQLプロパティに指定したSQL文を実行し、テーブルのデータを更新します。つまりRefreshメソッドを実行して初めてSelect文では「データが出力」され、Select文以外では「データの追加・変更・削除」されます。
なおQueryTableオブジェクト側には存在した「BackgroundQueryパラメータ」は、ListObjectオブジェクトには存在しません。調べてみると、ListObjectのRefreshメソッドは「BackgroundQuery = False」相当で、SQL文を実行しワークシートに出力が全て完了してから次のコードに移っているようです。と言って、Select文以外でも問題なく処理が行われます。

5-2-9.Deleteメソッド

DeleteメソッドもListObjectオブジェクト直下と合わせ、その下のQueryTableオブジェクトにも存在するようです。但し機能は異なります。
まずListObject直下のDeleteメソッドは、ListObjectオブジェクトが削除され「テーブルも削除」「ワークシート上の値も削除」されます。
一方QueryTableオブジェクトのDeleteメソッドは、ListObject内のQuerytableオブジェクトのみが削除され、値やテーブルは残る事になります。ですので、ワークシート上のテーブルや値をクリアする目的であれば「ListObject.Delete」を使う必要があります。

5-3.SQL文の指定方法

図12」や「接続情報の指定方法」で説明した通り、データベースへの接続方法は3通りが考えられます。またListObjectではSQL文の指定方法も2通りが考えられます。
ここでは接続情報の指定方法を「ドライバー(Driver=)」+「データベース(DBQ=)」に固定し、SQL文をどこに指定するかについて説明していきます。

5-3-1.CommandTextプロパティに指定

まず、SQL文をListObjectの中のQueryTable.CommandTextプロパティに指定するのが図48です。
  1. '========== ⇩(9) ListObjectオブジェクトのCommandTextプロパティに指定 ============
  2.  Dim ConStr As String    '接続文字列
  3.  Dim SqlStr As String    '実行するSQL文
  4.  Call ListQuery_Delete   '←既存のテーブルを削除
  5.  ConStr = "ODBC;"
  6.  ConStr = ConStr & "Driver={Microsoft Access Driver (*.mdb, *.accdb)};"
  7.  ConStr = ConStr & "DBQ=" & ThisWorkbook.Path & "¥" & "its-041.accdb;"
  8.  SqlStr = "select * from TestTable"
  9.  With Sheet1.ListObjects.Add(SourceType:=xlSrcQuery, Source:=ConStr, Destination:=Sheet1.Range("B2"))
  10.   .QueryTable.CommandText = SqlStr
  11.   .Refresh
  12.  End With
図48

190行目「SqlStr = "select * from TestTable"」でSQL文を作成します。
192行目「With Sheet1.ListObjects.Add(SourceType:=xlSrcQuery, Source:=ConStr, Destination:=Sheet1.Range("B2"))」では、Sheet1上にListObject(テーブル)を作成しています。
第1パラメータ(SourceType)には、第2パラメータ(Source)のタイプを指定しますので、xlSrcQuery(値=3)を設定します。なお、xlSrcExternal(値=0)でもOKです。
第2パラメータ(Source)には、ODBC接続情報を指定しますので、186~188行目で作成した変数ConStrを指定します。
第5パラメータ(Destination)には、出力データの書き込みセル範囲(範囲の左上セル位置)の指定をしています。
第3(LinkSource)、第4(xlListObjectHasHeaders)、第6(TableStyleName)パラメータは省略していますが、ODBCでは第3、第6パラメータは無効のようです。また第4パラメータはどの値を指定しても「データの先頭行はタイトル行」と判断されてしまうようなので、設定の意味がありません。
作成したテーブルに対し、193行目「.QueryTable.CommandText = SqlStr」では、ListObjectの中のQueryTable.CommandTextプロパティに190行目で作成したSQL文を指定しています。
194行目「.Refresh」でSQL文が実行され、ワークシート上にデータが出力されます。

5-3-2.SQLプロパティに指定

次に、SQL文をListObjectの中のQueryTable.SQLプロパティに指定するのが図49です。なお、ListObjectオブジェクトをいったんオブジェクト変数LObjとして取り出し、そのオブジェクト変数に対して処理を行っています。
  1. '========== ⇩(10) ListObjectオブジェクトのSQLプロパティに指定 ============
  2.  Dim LObj As ListObject    'ListObjectオブジェクト
  3.  Dim ConStr As String    '接続文字列
  4.  Dim SqlStr As String    '実行するSQL文
  5.  Call ListQuery_Delete   '←既存のテーブルを削除
  6.  ConStr = "ODBC;"
  7.  ConStr = ConStr & "Driver={Microsoft Access Driver (*.mdb, *.accdb)};"
  8.  ConStr = ConStr & "DBQ=" & ThisWorkbook.Path & "¥" & "its-041.accdb;"
  9.  SqlStr = "select * from TestTable"
  10.  Set LObj = Sheet1.ListObjects.Add(SourceType:=3, Source:=ConStr, Destination:=Sheet1.Range("B2"))
  11.  With LObj
  12.   .QueryTable.SQL = SqlStr
  13.   .Refresh
  14.  End With
  15.  Set LObj = Nothing
図49

221行目「SqlStr = "select * from TestTable"」で、SQL文を作成します。
223行目「Set LObj = Sheet1.ListObjects.Add(SourceType:=3, Source:=ConStr, Destination:=Sheet1.Range("B2"))」では、Sheet1上にListObject(テーブル)を作成しています。
作成したListObjectオブジェクト(変数LObj)に対し、226行目「.QueryTable.SQL = SqlStr」で、SQLプロパティに221行目で作成したSQL文を指定しています。この場合のSQLプロパティも、ListObjectの中のQueryTableオブジェクトのプロパティです。
227行目「.Refresh」で指定したSQL文が実行され、ワークシート上にデータが出力されます。
先頭の211行目では、変数LObjを「Dim LObj As ListObject」と宣言しています。データ型としてListObject型の代わりにObject型を使用し「Dim LObj As Object」と宣言してもOKです。

5ー4.ワークシート上のListObjectの削除

図48の184行目、図49の215行目では「Call ListQuery_Delete」と、図34の「既存のテーブル類を削除」するプロシージャを呼び出しています。この作業を行わないと、QueryTableオブジェクトの時にはテーブルが横方向に重なっていく(図33)のような現象が発生していましたが、ListObjectオブジェクトの場合は「テーブルを重ねることはできません」との実行時エラーが発生してしまいます。
ですのでListObject.Deleteメソッドを使い、既存のテーブルを削除する必要があります。
コードとしては、図34の119~121行目のような形になります。

5ー5.データベース種と操作種の違いによる操作方法

上記では接続データベースをAccessに固定し、またSQL文もSelect文のみでした。しかしListObjectを使ったデータベース接続ではAccess以外のExcelやCSVにも接続可能ですし、またInsert文・Update文・Delete文も実行可能です。
そこで図50では、様々なデータベース、および様々なSQL文に対応したコードを紹介します。
なお、ここではデータベース接続には「ドライバー名」+「データベース名」を指定し、SQL文はListObject内のQueryTable.CommandTextプロパティに指定することとしますが、「接続情報の指定方法」「SQL文の指定方法」で説明した通り、別な方法を使ってもOKです。
ここで扱うデータは、QueryTableの時にも使用した図35のような「4列×4行」としています。St、Nu、Dt、BLが列名であり、Excel・CSVも先頭行は列名行としています。
また図50では、Excelのシートデータベースについて説明していきますが、QueryTableの時と同様(図36)にコードを切り替えることで、データベースをAccess・CSVに、またSQL文を選択・挿入・変更・削除に切り替えることが出来ます。
なおAccessは全ての操作(Select・Insert・Update・Delete)が可能ですが、ExcelはDelete操作は不可(Select・Insert・Update操作のみ可)、CSVはUpdate・Delete操作が不可(Select・Insert操作のみ可)となります。
  1. '========== ⇩(11) 各データベースへの接続・操作 ============
  2. Sub ODBC_ListObject01()
  3.  Dim LObj As ListObject    '←ListObjectオブジェクト
  4.  Dim ConStr As String     '←接続文字列
  5.  Dim SqlStr As String      '←'実行するSQL文
  6.  Call ListQuery_Delete     '←既存のテーブルを削除
  7.  ConStr = "ODBC;"
  8. ' ConStr = ConStr & "Driver={Microsoft Access Driver (*.mdb, *.accdb)};"   '←①Accessドライバー情報
  9. ' ConStr = ConStr & "DBQ=" & ThisWorkbook.Path & "¥" & "its-041.accdb;"   '←④Accessデータベース情報
  10.  ConStr = ConStr & "Driver={Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)};"   '←②Excelドライバー情報
  11.  ConStr = ConStr & "DBQ=" & ThisWorkbook.Path & "¥" & ThisWorkbook.Name & ";"   '←⑤Excelデータベース情報
  12. ' ConStr = ConStr & "Driver={Microsoft Access Text Driver (*.txt, *.csv)};"   '←③CSVドライバー情報
  13. ' ConStr = ConStr & "DBQ=" & ThisWorkbook.Path & "¥;"     '←⑥CSVデータベース情報
  14.  ConStr = ConStr & "ReadOnly=0;"
  15. ' SqlStr = "TestTable"   '←⑦Accessテーブル
  16.  SqlStr = "[db$]"      '←⑧Excelテーブル(シート名)
  17. ' SqlStr = "[its-041.csv]"   '←⑨CSVテーブル(ファイル名)
  18.  SqlStr = "select * from " & SqlStr   '←⑩Select文
  19. ' SqlStr = "insert into " & SqlStr & " (st,nu,dt,bl) values ('PQR',897,#11/20/2000#,True)"   '←⑪Inset文
  20. ' SqlStr = "Update " & SqlStr & " set St='XYZ' where DT=#2000/11/20#"     '←⑫Update文
  21. ' SqlStr = "Delete from " & SqlStr & " where Dt=#11/20/2000#"     '←⑬Delete文
  22.  Set LObj = Sheet1.ListObjects.Add( _
  23.       SourceType:=3, _
  24.       Source:=ConStr, _
  25.       Destination:=Sheet1.Range("B2") _
  26.       )
  27.  With LObj
  28. '  .ShowHeaders = True   '←タイトル行表示有無。True(既定)=表示有り
  29. '  .ShowAutoFilter = True   '←フィルターボタン表示有無。True(既定)=表示有り
  30. '  .TableStyle = "TableStyleMedium2"   '←テーブルスタイル。既定は"TableStyleMedium2"
  31.   .QueryTable.CommandText = SqlStr   '←実行するSQL文
  32. '  .QueryTable.SQL = SqlStr   '←実行するSQL文
  33. '  .QueryTable.CommandType = xlCmdSql   '←CommandTextの種類。ODBCではxlCmdSql(値=2)が既定
  34. '  .QueryTable.AdjustColumnWidth = True   '←列幅の調整。True(既定)=自動調整
  35. '  .QueryTable.RowNumbers = False     '←行番号の表示有無。False(既定)=表示無し
  36.   .Refresh     '←実行・データ更新
  37. '  .QueryTable.Refresh     '←実行・データ更新
  38.  End With
  39.  Set LObj = Nothing
  40. End Sub
図50

246行目「Call ListQuery_Delete」は図34のプロシージャを呼び出し、「ワークシート上の既存のテーブルを削除」しています。これから作成するテーブルのみがワークシート上にある状態にするためです。
248~258行目では、データベースに接続するための接続文字列(変数ConStr)を組み立てています。
まず文字列の先頭にはODBC接続を示すため、248行目「ConStr = "ODBC;"」とします。
252行目「ConStr = ConStr & "Driver={Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)};"」では、文字列にドライバー名を追加指定しています。
253行目「ConStr = ConStr & "DBQ=" & ThisWorkbook.Path & "¥" & ThisWorkbook.Name & ";"」では文字列にデータベース名を更に追加指定しています。 なお、Accessに接続する場合は249~250行目を、CSVの場合は255~256行目のドライバーとデータベース名を使用します。
258行目「ConStr = ConStr & "ReadOnly=0;"」では、読取/書込の条件として「ReadOnly=0」を指定し、書き込み可能状態にしています。図18の通り、このReadOnlyを省略してしまうとExcelでは読取専用となってしまうので、Insert文などを実行するときには必須となります。
260~267行目では、処理を行うSQL文(変数SqlStr)を組み立てています。
今回はExcelのデータベースに接続しますので、まずテーブル名を変数SqlStrに格納するため261行目「SqlStr = "[db$]"」でExcelのワークシート名を指定します。この時、シート名には「$印」を付けた上で全体を角カッコで囲みます。この指定方法については、図25を参照下さい。また、Accessの場合は260行目、CSVの場合は262行目のようになります。
264行目「SqlStr = "select * from " & SqlStr」では、Select文を組み立てています。この時のテーブル名は261行目で準備した文字列を使用します。なおFrom句とテーブル名の間にスペースを入れる事を忘れないようにして下さい。
Select文以外は265~267行目のInsert文・Update文・Delete文を参照下さい。なお、Accessでは全ての操作が可能ですが、ExcelではDelete文は不可です。またCSVではUpdate文・Delete文が不可となります。
269~273行目「Set LObj = Sheet1.ListObjects.Add(SourceType:=3, Source:=ConStr, Destination:=Sheet1.Range("B2"))」では、ListObjectオブジェクトを作成しています。
270行目の第1パラメータ「SourceType:=3」では、第2パラメータの種類を指定します。今回はODBCの接続文字列ですので、xlScrQuery(値=3)とします。なおxlScrExternal(値=0)でもOKです。
271行目の第2パラメータ「Source:=ConStr」は、今回はODBCのデータベース接続文字列(248~258行目で組み立てたConStr変数)を指定します。
272行目の第5パラメータ「Destination:=Sheet1.Range("B2")」は、出力データの書き込みセル範囲の左上セル位置を指定します。
275~286行目では、ListObjectオブジェクトへの設定とSQL文の実行を行います。
まず275行目「With LObj」より後ろでは、作成したListObjectオブジェクト(変数LObj)に対しての処理を行います。
279行目「.QueryTable.CommandText = SqlStr」では、ListObject内のQueryTable.CommandTextプロパティに、260~267行目で組み立てたSQL文(変数SqlStr)を指定します。
284行目「.Refresh」では、ListObjectのRefreshメソッドにより、設定したSQL文を実行しています。ここではSQL文はSelect文ですので、Refreshメソッドによりワークシートにデータが出力されることになります。
なお、276~278行目・282~283行目の見え消しにしてあるプロパティ設定を調整することで、出力されるテーブルの書式等を変更することが可能です。
なお280行目「.QueryTable.SQL = SqlStr」は、279行目と同じ機能ですので、どちらかでSQL文の設定を行います。また281行目「.QueryTable.CommandType = xlCmdSql」のCommandTypeプロパティは xlCmdSql(値=2)が既定です。xlCmdSql以外を指定してしまうとエラーとなりますので注意が必要です。
285行目「.QueryTable.Refresh」は284行目「.Refresh」と同じ機能のようです。どちらを使用しても同じ結果となります。

6.出力される表示形式

出力されるデータの表示形式については、本文中でも触れていたつもりですが、ここで整理しておきます。

6-1.QueryTableオブジェクト

まずQueryTableオブジェクトを使って出力した場合が図51です。上段が図35の元データの出力、下段がInsert文(図37の156行目)でデータを挿入後に再度出力したものです。
QueryTableによる出力例
図51

まず日付型(Dt列)は、元データでは「日付のみ」なのですが、「時刻(hh:mm)」も出力されます。データを挿入してもその表示形式は変わりません。
またExcelへの保存データは「日付のみであれば、日付のみで記録」されますが、CSVでは「必ず日付+時刻」で記録されるようです。
一方Boolean型(BL列)についてはちょっと複雑です。
まず元データ上の書式ですが、Accessでは当然Boolean型で格納されているはずです。またExcelでは、文字列として「True/False」とセル入力しましたが、Excel側が値からデータ型を解釈しBoolean型として表示されています。CSVでは「"(ダブルクォーテーション)」無しのTrue/Falseという文字列を今回入力してみました。
このBoolean型を出力すると、Access・Excelでは「True → 1」「False → 0」という形で表示されます。Excelでは「Trueは-1」という意識がありますので注意が必要です。
またCSVの場合は「True/False」というそれらしい表示になっていますが、True/Falseという文字列をそのまま文字列として出力(シートへ貼付け)しただけで、Excel側が勝手に「この文字列はBoolean型」と解釈し表示しているようです。
ですのでデータ挿入されたCSVファイルを見てみると、「"-1"」という「"(ダブルクォーテーション)」付きの文字列で記録されており、記録時にExcel側が「Trueは-1」と解釈してCSVファイルに書き込んでいる事がわかります。そのため再出力時は「-1という数値」として出力してしまいます。
一方Excelの挿入された元データ側は、正しく「True/False として記録」されています。
Boolean型はデータベースにより扱いが異なるので、特に注意が必要そうです。

6-2.ListObjectオブジェクト

次にListObjectオブジェクトを使って出力した場合が図52です。上段が図35の元データの出力、下段がInsert文(図50の265行目)でデータを挿入後に再度出力したものです。
ListObjectによる出力例
図52

ListObject型で注意しなければいけないのは、日付型(Dt列)です。出力したデータは全てのデータベースで「数値」として表示されます。
データ挿入後の元データを確認しましたが、Excel・CSVとも「日付型」として記録されています(Accessは当然日付型)が、出力されると数値型になるようです。
出力データを日付型として取り込めば処理上は問題ないかと思いますが、表示時には注意が必要そうです。
なお、Boolean型(BL列)については、QueryTableオブジェクトと全く同じです。

アプリ実例・関連する項目

ListObjectの作成と概要

サンプルファイル

ODBCでAccess・Excel・CSVへ接続・操作(its-044.xlsm)
Accessデータファイル(its-041.accdb)
CSVデータファイル(its-041.csv)
セキュリティ向上を目的として「インターネット経由でダウンロードしたOfficeファイル(Excel等)のマクロは、既定でブロック」されるようにOfficeアプリケーションの既定動作が変更になりました。(2022年4月より切替開始)
解除の方法については「ダウンロードファイルのブロック解除方法」を参照下さい。