【VBAリファレンス】SQL入門「ADO + VBA」でSQLを実行するときのVBAサンプル

スポンサーリンク

概要

本記事では、Excel VBAからActiveX Data Objects (ADO) を利用してSQLを実行する方法について、初心者の方にも分かりやすく解説します。データベース操作の基本となるSQL文をVBAから呼び出し、データの取得、更新、削除といった一連の操作を効率的に行うための実践的なテクニックを、具体的なサンプルコードと共に紹介します。

ADOは、Microsoftが提供するデータベースアクセス技術であり、ODBCドライバーなどを介して様々なデータベース(Access, SQL Server, Oracleなど)に接続し、SQL文を実行することができます。VBAとADOを組み合わせることで、Excelをフロントエンドとして、強力なデータベース管理システムを構築することが可能になります。

本記事を読むことで、以下の内容を習得できます。

* ADOの基本的な概念とVBAでの利用方法
* データベースへの接続方法
* SQL文(SELECT, INSERT, UPDATE, DELETE)のVBAからの実行方法
* 取得したデータをExcelシートに表示する方法
* エラーハンドリングの重要性

これらの知識を習得することで、Excelでのデータ集計やレポート作成の自動化、さらには簡易的な業務アプリケーションの開発へとステップアップするための基盤を築くことができます。

詳細解説

ADOオブジェクトモデルの理解

ADOを利用するには、まずADOの主要なオブジェクトについて理解することが重要です。

* **Connectionオブジェクト**: データベースへの接続を管理します。接続文字列を設定し、データベースとの通信を確立・切断します。
* **Commandオブジェクト**: 実行したいSQL文やストアドプロシージャを定義します。Connectionオブジェクトと関連付けられ、SQL文を実行します。
* **Recordsetオブジェクト**: SQL文(特にSELECT文)の実行結果を格納します。レコードセット内のデータを操作したり、Excelシートに表示したりします。

これらのオブジェクトを連携させることで、データベース操作を実現します。

データベースへの接続

データベースに接続するには、`ADODB.Connection` オブジェクトを作成し、`Open` メソッドを使用します。`Open` メソッドには、接続文字列を指定します。接続文字列は、使用するデータベースの種類や接続方法によって異なります。

例えば、Microsoft Accessデータベース(.mdbまたは.accdbファイル)に接続する場合の接続文字列の例は以下のようになります。

Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\path\to\your\database.accdb;

* `Provider`: 使用するOLE DBプロバイダーを指定します。Accessの場合は `Microsoft.ACE.OLEDB.12.0` (または古いバージョンでは `Microsoft.Jet.OLEDB.4.0`) を指定します。
* `Data Source`: データベースファイルへのフルパスを指定します。

SQL Serverに接続する場合は、以下のような接続文字列になります。

Provider=SQLOLEDB;Server=your_server_name;Database=your_database_name;Uid=your_username;Pwd=your_password;

接続が成功すると、`Connection` オブジェクトの状態が `adStateOpen` になります。

SQL文の実行(SELECT文)

データを取得するには、`SELECT` 文を実行し、結果を `Recordset` オブジェクトに格納します。

1. `ADODB.Connection` オブジェクトを作成し、データベースに接続します。
2. `ADODB.Recordset` オブジェクトを作成します。
3. `Recordset` オブジェクトの `Open` メソッドに、実行したいSQL文と、作成済みの `Connection` オブジェクトを渡します。
4. `Recordset` オブジェクトには、取得したデータが格納されます。

`Recordset` オブジェクトのプロパティやメソッドを使用して、データを操作します。

* `RecordCount`: レコード数を取得します。
* `Fields`: 各フィールド(列)にアクセスします。`rs.Fields(“FieldName”).Value` や `rs.Fields(0).Value` のようにアクセスできます。
* `MoveNext`: 次のレコードに移動します。
* `EOF` (End Of File): レコードセットの末尾に達したかどうかを示すブール値です。

取得したデータをExcelシートに表示するには、`Recordset` オブジェクトの `GetRows` メソッドを利用するのが効率的です。`GetRows` メソッドは、レコードセットの全レコードを二次元配列として返します。この配列をExcelシートのセル範囲に一括で代入することで、高速なデータ表示が可能です。

SQL文の実行(INSERT, UPDATE, DELETE文)

データの追加(INSERT)、更新(UPDATE)、削除(DELETE)といったデータ操作言語(DML)を実行する場合、`Command` オブジェクトを利用するのが一般的です。

1. `ADODB.Connection` オブジェクトを作成し、データベースに接続します。
2. `ADODB.Command` オブジェクトを作成します。
3. `Command` オブジェクトの `ActiveConnection` プロパティに、作成済みの `Connection` オブジェクトを設定します。
4. `Command` オブジェクトの `CommandText` プロパティに、実行したいSQL文を設定します。
5. `Command` オブジェクトの `CommandType` プロパティを `adCmdText` に設定します(SQL文を実行する場合)。
6. `Command` オブジェクトの `Execute` メソッドを実行します。このメソッドは、実行したレコード数を返します。

`INSERT`, `UPDATE`, `DELETE` 文は、通常、結果セットを返さないため、`Recordset` オブジェクトは不要です。

エラーハンドリング

データベース操作は、ネットワークの問題、権限不足、SQL文の誤りなど、様々な原因でエラーが発生する可能性があります。そのため、適切なエラーハンドリングは非常に重要です。

VBAでは、`On Error GoTo` ステートメントを使用して、エラー発生時の処理を記述します。エラーハンドララベル(例: `ErrHandler:`)をコードの最後に配置し、エラー発生時にジャンプするようにします。エラーハンドラ内では、`Err` オブジェクトのプロパティ(`Err.Number`, `Err.Description`)を使用して、エラーの詳細を取得し、ユーザーに通知したり、ログに記録したりすることができます。

また、トランザクション処理を適切に行うことで、複数のデータベース操作をまとめて実行し、いずれかの操作でエラーが発生した場合に、それまでの操作をすべて元に戻す(ロールバック)ことができます。これにより、データの整合性を保つことができます。

* `Connection.BeginTrans`: トランザクションを開始します。
* `Connection.CommitTrans`: トランザクションをコミット(確定)します。
* `Connection.RollbackTrans`: トランザクションをロールバック(取り消し)します。

サンプルコード

ここでは、Microsoft Accessデータベース(`C:\db\SampleDB.accdb`)に接続し、`Products` テーブルからデータを取得してExcelシートに表示するVBAコードの例を示します。

まず、VBAエディタで「ツール」->「参照設定」を開き、「Microsoft ActiveX Data Objects x.x Library」にチェックを入れてください。(x.xはバージョン番号です。通常は最新のものを選択します。)

Sub GetProductsData()

Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim sql As String
Dim ws As Worksheet
Dim lngRow As Long
Dim vData As Variant

‘ データベース接続情報
Const DB_PATH As String = “C:\db\SampleDB.accdb” ‘ 実際のデータベースパスに変更してください
Const CONN_STR As String = “Provider=Microsoft.ACE.OLEDB.12.0;Data Source=” & DB_PATH & “;”

‘ 接続オブジェクトの初期化
Set cn = New ADODB.Connection
‘ エラーハンドリングの設定
On Error GoTo ErrorHandler

‘ データベースに接続
cn.Open CONN_STR
Debug.Print “データベースに接続しました。”

‘ Recordsetオブジェクトの初期化
Set rs = New ADODB.Recordset

‘ 取得するSQL文
sql = “SELECT ProductID, ProductName, Price FROM Products ORDER BY ProductName;”

‘ SQLを実行し、結果をRecordsetに格納
‘ Recordset.Open SQL, Connection, CursorType, LockType
rs.Open sql, cn, adOpenForwardOnly, adLockReadOnly

‘ 取得したデータをExcelシートに表示
Set ws = ThisWorkbook.Sheets(“Sheet1”) ‘ 表示先のシート名を指定
ws.Cells.ClearContents ‘ シートの内容をクリア

‘ ヘッダー行の書き込み
ws.Cells(1, 1).Value = “商品ID”
ws.Cells(1, 2).Value = “商品名”
ws.Cells(1, 3).Value = “価格”

‘ データの取得とシートへの書き込み
If Not rs.EOF Then
‘ GetRowsメソッドで全データを二次元配列として取得
vData = rs.GetRows
‘ 配列のサイズを取得 (行数, 列数)
Dim numRows As Long
Dim numCols As Long
numRows = UBound(vData, 2) + 1 ‘ GetRowsは0から始まるため+1
numCols = UBound(vData, 1) + 1

‘ Excelシートの適切な位置に配列を書き込み
ws.Cells(2, 1).Resize(numRows, numCols).Value = Application.Transpose(vData) ‘ Transposeで列と行を入れ替えて代入
Else
MsgBox “データが見つかりませんでした。”, vbInformation
End If

Debug.Print “データをExcelシートに表示しました。”

‘ 後処理:RecordsetとConnectionを閉じる
rs.Close
Set rs = Nothing
cn.Close
Set cn = Nothing

Exit Sub

‘ エラーハンドラ
ErrorHandler:
MsgBox “エラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“エラー内容: ” & Err.Description, vbCritical
‘ エラー発生時もリソースを解放する
If Not rs Is Nothing Then
If rs.State = adStateOpen Then rs.Close
Set rs = Nothing
End If
If Not cn Is Nothing Then
If cn.State = adStateOpen Then cn.Close
Set cn = Nothing
End If

End Sub

‘ データベースにデータを挿入するサンプル
Sub InsertProductData()

Dim cn As ADODB.Connection
Dim cmd As ADODB.Command
Dim sql As String
Dim recordsAffected As Long

‘ データベース接続情報 (上記と同じ)
Const DB_PATH As String = “C:\db\SampleDB.accdb”
Const CONN_STR As String = “Provider=Microsoft.ACE.OLEDB.12.0;Data Source=” & DB_PATH & “;”

‘ 接続オブジェクトとCommandオブジェクトの初期化
Set cn = New ADODB.Connection
Set cmd = New ADODB.Command
On Error GoTo ErrorHandler

‘ データベースに接続
cn.Open CONN_STR
Debug.Print “データベースに接続しました。”

‘ Commandオブジェクトの設定
Set cmd.ActiveConnection = cn
cmd.CommandType = adCmdText ‘ SQL文を実行することを指定
‘ 挿入するSQL文 (パラメータ化クエリを使用すると、SQLインジェクション対策にもなり安全です)
sql = “INSERT INTO Products (ProductName, Price) VALUES (‘新しい商品’, 1500);”
cmd.CommandText = sql

‘ SQL文を実行
cmd.Execute recordsAffected ‘ recordsAffected には影響を受けたレコード数が格納される

Debug.Print recordsAffected & “件のデータが挿入されました。”

‘ 後処理
Set cmd = Nothing
cn.Close
Set cn = Nothing

Exit Sub

ErrorHandler:
MsgBox “エラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“エラー内容: ” & Err.Description, vbCritical
If Not cmd Is Nothing Then Set cmd = Nothing
If Not cn Is Nothing Then
If cn.State = adStateOpen Then cn.Close
Set cn = Nothing
End If

End Sub

**注意点:**

* 上記のコードは、`C:\db\SampleDB.accdb` というパスにMicrosoft Accessデータベースが存在し、`Products` というテーブルに `ProductID`, `ProductName`, `Price` という列が存在することを前提としています。ご自身の環境に合わせてパスやテーブル名、列名を変更してください。
* `Microsoft.ACE.OLEDB.12.0` プロバイダーがインストールされていない場合、エラーが発生します。Microsoft Access Database Engine Redistributable をインストールしてください。
* `GetRows` メソッドで取得したデータは、VBAのVariant型配列に格納されます。Excelシートに代入する際に、`Application.Transpose` を使用して行と列を入れ替える必要がある場合があります。これは、`GetRows` が「列を行」としてデータを格納するのに対し、Excelシートは「行を列」としてデータを扱えるように配列を期待するためです。
* `adOpenForwardOnly` は、前方スクロールのみ可能なカーソルタイプで、最も効率的です。`adLockReadOnly` は、読み取り専用ロックを指定します。
* `INSERT` サンプルでは、直接SQL文に値を埋め込んでいますが、実際のアプリケーションでは、ユーザー入力値などを直接SQL文に埋め込むとSQLインジェクションのリスクがあります。`ADODB.Parameter` オブジェクトを使用したパラメータ化クエリの使用を強く推奨します。

実務アドバイス

1. **接続文字列の管理:** 接続文字列は、データベースの種類やサーバー名、認証情報など、機密情報を含む場合があります。ハードコーディングは避け、設定ファイルやExcelのシート、あるいはWindowsのレジストリなどで管理することを検討しましょう。
2. **パラメータ化クエリの活用:** SQLインジェクション攻撃を防ぎ、コードの可読性と保守性を向上させるために、`Command` オブジェクトの `Parameters` コレクションを使用したパラメータ化クエリを積極的に使用してください。

‘ パラメータ化クエリの例
Dim cmd As ADODB.Command
Set cmd = New ADODB.Command
cmd.ActiveConnection = cn
cmd.CommandType = adCmdText
cmd.CommandText = “SELECT ProductName, Price FROM Products WHERE ProductID = ?” ‘ パラメータマーカー

Dim prmID As ADODB.Parameter
Set prmID = cmd.CreateParameter(“@ProductID”, adInteger, adParamInput, , 101) ‘ パラメータの作成
cmd.Parameters.Append prmID ‘ パラメータの追加

Set rs = cmd.Execute()

3. **適切なカーソルタイプとロックタイプの選択:** `Recordset.Open` メソッドの引数で指定するカーソルタイプ (`CursorTypeEnum`) とロックタイプ (`LockTypeEnum`) は、パフォーマンスに大きく影響します。
* **CursorType:**
* `adOpenForwardOnly`: 最も高速。順方向への移動のみ。
* `adOpenKeyset`: サーバー側でキーセットを保持。レコードの追加・削除は反映されないが、更新は反映される。
* `adOpenDynamic`: 動的。レコードの追加・削除・更新がすべて反映される。
* `adOpenStatic`: 静的。データはスナップショット。
* **LockType:**
* `adLockReadOnly`: 読み取り専用。
* `adLockPessimistic`: 他のユーザーがレコードを編集できないようにロック。
* `adLockOptimistic`: 編集終了時にロック。
* `adLockBatchOptimistic`: バッチ更新用。

通常、単にデータを表示するだけなら `adOpenForwardOnly` と `adLockReadOnly` が最も効率的です。
4. **トランザクション処理の徹底:** 複数のデータ操作をまとめて実行する場合(例:注文処理で在庫を減らし、注文履歴を記録するなど)、必ずトランザクション処理を行い、データの整合性を確保してください。
5. **オブジェクトの解放:** 使用し終わったADOオブジェクト(`Connection`, `Recordset`, `Command` など)は、必ず `Close` メソッドで閉じ、`Set obj = Nothing` で解放してください。これにより、メモリリークやリソースの枯渇を防ぐことができます。特にエラー発生時にもこれらの解放処理が確実に行われるように、エラーハンドラ内でもオブジェクトの解放処理を記述することが重要です。
6. **パフォーマンスの考慮:**
* 大量のデータを取得する場合は、`GetRows` メソッドで一度に取得し、Excelシートに一括で書き込むのが最も高速です。
* 不要な列は `SELECT` 文で指定しないようにしましょう。
* データベース側でインデックスを適切に設定することで、SQL文の実行速度を大幅に向上させることができます。
7. **デバッグの活用:** `Debug.Print` ステートメントをコードの要所に挿入することで、変数の値や処理の進行状況をイミディエイトウィンドウに表示し、デバッグに役立てることができます。

まとめ

本記事では、Excel VBAからADOを使用してSQLを実行するための基本的な手順と、具体的なサンプルコードを解説しました。

* `ADODB.Connection` オブジェクトでデータベースに接続し、
* `ADODB.Recordset` オブジェクトで `SELECT` 文の結果を取得し、
* `ADODB.Command` オブジェクトで `INSERT`, `UPDATE`, `DELETE` 文を実行する

という流れを理解することが、VBAによるデータベース操作の第一歩となります。

ADOとVBAを組み合わせる

タイトルとURLをコピーしました