【Excel・Access VBA】ADOでストアドを実行してSELECT結果を受け取る方法

VBA
スポンサーリンク

今回の記事では、VBAからADOを介してSQL Serverのストアドを実行し、ストアド内でSELECTした表をVBA側に返す一連の処理のサンプルプログラムを紹介します。
恐らくSQL Server以外のRDBMSで作成したストアドでも使えるとは思いますが、SQL Server以外は未検証です。

尚、以前の記事で、VBAからADOを介してストアドを実行して、その実行結果を受け取る処理のサンプルコードも紹介しています。
ストアド内で実行したSELECT結果を受け取るのではなく、ストアドを実行してRETURNの値を受け取りたい場合は以下の記事を参考にしてください。

 
 

事前準備:DB接続用クラスの実装

過去の記事で、VBAでADOを使用してデータベースに接続する処理をクラス化したサンプルプログラムを紹介させていただいており、今回の記事では、そのクラスに対して、ストアドを実行してSELECT結果を受け取るメソッドを追加します。
そのため、以下の記事で紹介しているクラスをVBAのプロジェクトに追加してください。

尚、このデータベース接続用クラスを流用するのは、データベースとのConnectionを使いまわすだけの目的であり、Connectionを新しく作るのであれば、こちらのクラスを使用しなくても問題はありません。
 
 

サンプルプログラム

当項では、ストアド側のサンプルプログラムとVBA側のサンプルプログラムをそれぞれ紹介します。
尚、重要なのはVBA側です。
 

ストアド側サンプルプログラム

今回の記事の検証のためにストアドを作成したRDBMSはSQL Serverです。
ストアド側でのポイントは、処理の最後に返したい表のSELECT文を実行しておくことです。
そのSELECT結果を変数に入れるなどの特別な処理は不要です(※SQL Server以外は試していないので不明)。

ストアド側のサンプルコートでは、ストアド名を「uspSelectTest」としています。
必要によって名前を変更して使用してください。

CREATE PROCEDURE [dbo].[uspSelectTest]
	@para1	int
AS
BEGIN

	--なんやかんやの処理開始

	--なんやかんやの処理ここまで

	--クライアントに返す表を作成します。
	SELECT * FROM 対象テーブル WHERE 列名1 = @para1;

	RETURN 0

	--例外処理
	ERR:
		RETURN 1

END

GO

 

VBA側ストアド実行用クラス サンプルプログラム

前述したデータベース接続用クラスを使用する場合は、そのクラス内に以下のサンプルコードを丸っとコピペして追加してください。

'引数のストアド名とパラメーターを使用して、ストアドを実行します。
'引数1:ストアド名
'引数2:パラメーター※2次元配列(パラメータ名,データ型,値) パラメーターは5つまで。
'戻り値:ストアド内で作成した表を戻り値にセット
Public Function execute_usp_select(SpName As String, Param() As String) As Variant

Dim cmd As New ADODB.Command
Dim Param1 As New ADODB.Parameter
Dim Param2 As New ADODB.Parameter
Dim Param3 As New ADODB.Parameter
Dim Param4 As New ADODB.Parameter
Dim Param5 As New ADODB.Parameter
Dim i As Integer

    
    Set cmd.ActiveConnection = mCon
    'コマンドタイプにストアドを指定します。
    cmd.CommandType = adCmdStoredProc
    'ストアド名をセットします。
    cmd.CommandText = SpName

    '引数のパラメーター配列を要素数分ループします。
    For i = LBound(Param, 1) To UBound(Param, 1)
    
        'パラメータが5以上(カウンタが4以上)の場合はループから抜けます。
        If i > 4 Then
            Exit For
        End If
        
        'パラメーターをセットします。
        Select Case i
            Case 0
                Set Param1 = cmd.CreateParameter(Param(0, 0), Param(0, 1), adParamInput)
                Param1.Value = Param(0, 2)
                cmd.Parameters.Append Param1
            Case 1
                Set Param2 = cmd.CreateParameter(Param(1, 0), Param(1, 1), adParamInput)
                Param2.Value = Param(1, 2)
                cmd.Parameters.Append Param2
            Case 2
                Set Param3 = cmd.CreateParameter(Param(2, 0), Param(2, 1), adParamInput)
                Param3.Value = Param(2, 2)
                cmd.Parameters.Append Param3
            Case 3
                Set Param4 = cmd.CreateParameter(Param(3, 0), Param(3, 1), adParamInput)
                Param4.Value = Param(3, 2)
                cmd.Parameters.Append Param4
            Case 4
                Set Param5 = cmd.CreateParameter(Param(4, 0), Param(4, 1), adParamInput)
                Param5.Value = Param(4, 2)
                cmd.Parameters.Append Param5
        End Select
        
    Next i

	'ストアド内で取得した表を戻り値にセットします。
    Set execute_usp_select = cmd.execute

    Set Param5 = Nothing
    Set Param4 = Nothing
    Set Param3 = Nothing
    Set Param2 = Nothing
    Set Param1 = Nothing

    Set cmd = Nothing

End Function

 
上記の処理は、冒頭でお伝えした、過去に記事にしているVBAのADOからストアドを実行する処理を流用し、わずかに変更して実装しています。
 

VBA側クラス呼び出し サンプルプログラム

上記のクラスの呼び出し例としてのサンプルプログラムを紹介します。
サンプルコードでは、Subプロシージャ名を「Sample」としています。
こちらも必要によって変更してください。
また、ストアドに渡すパラメーターは配列ですが、コメントにもあるようにパラメーターを増やしたければ、添え字を必要によって変更してください。

Sub Sample()

Dim db As test_project.DataBaseAccess
Dim Rs As ADODB.Recordset
Dim strSQL As String
Dim strSpName As String
Dim strPara(0, 2) As String	'必要により添え字を変更

    Set db = test_project.DatabaseFactory.creater

    'サーバデータベースと接続します。
    db.connect

    strSpName = "dbo.uspSelectTest"

    'パラメーター1
    strPara(0, 0) = "para1"	'パラメーター名
    strPara(0, 1) = adBigInt	'データ型
    strPara(0, 2) = 1111	'値
    'パラメーター2 ※必要によってコメントを外して使用してください。
    'strPara(1, 0) = "para2"	'パラメーター名
    'strPara(1, 1) = adBigInt	'データ型
    'strPara(1, 2) = 0		'値

    'データが無ければレコードセットはEOFになります。
    Set Rs = db.execute_select_usp(strSpName, strPara)
    
    Do Until Rs.EOF
        Debug.Print Rs(0).Value & " | " & Rs(1).Value & " | " & Rs(2).Value & " | " & Rs(3).Value & " | " & Rs(4).Value
        Rs.MoveNext
    Loop

    Set rs = Nothing
    db.disconnect
    Set db = Nothing
End Sub

 
この処理では、ストアドを実行して、そのストアド内で実行したSELECT結果をレコードセットオブジェクトで受け取っています。
SELECTした際にレコードが存在しなければ、そのレコードセットはEOFになります。

後はレコードセットなので、ループで回すなりしてレコードを取り出します。
簡単ですね!
 
 
 

【番外編】もう一つのサンプルプログラム

この記事を書いた当初は、ストアドで実行したSELECT結果をVBA側に戻せないと勘違いしていたので、その時に作成したサンプルプログラムも参考に掲載しておきます。

こちらのサンプルでは、冒頭で紹介した、VBAのADOを介してストアドを実行する処理で実装しているストアド実行用メソッドを使用してストアドを実行し、そのストアド内で一時テーブルを作成して、ストアド実行後に同じセッション内でその一時テーブルを参照するといった実装をしています。
一時テーブルの扱い方の参考になるかも知れません。
 
 

【番外編】サンプルプログラムの処理の流れ

今回のサンプルプログラムでは、ストアド側の処理と、VBAでADOを使用してストアドを呼び出す側の処理をそれぞれ紹介します。

ストアド側では、ストアド内で一時テーブルを作成し、その一時テーブルに対してVBA側に返したいデータをSELECTしてINSERTしています。

VBA側では対象のストアドをADOを介して呼び出した後に、同じコネクションのなかでストアド内で作成された一時テーブルをSELECTして、そのSELECT結果をレコードセットに格納しています。
 

一時テーブルを使用したサンプルプログラム

当項では、SQL Server内のストアドで作成した一時テーブルをVBA側のADOで参照させる処理のサンプルプログラムを紹介します。
 

ストアド側のサンプルプログラム

CREATE PROCEDURE [dbo].[uspSelectTest]
  @para1	int
AS
BEGIN

  --なんやかんやの処理開始

  --なんやかんやの処理ここまで

  --クライアントに渡す一時テーブルを作成します。
  CREATE TABLE ##tempテーブル (
  列名1 int,
  列名2 int,
  列名3 int,
  列名4 int,
  列名5 nvarchar(30)

  )
  --一時テーブルにデータを追加します。
  INSERT INTO ##tempテーブル(列名1,列名2,列名3,列名4,列名5)
  SELECT 列名1,列名2,列名3,列名4,列名5
  FROM スキーマ名.対象テーブル名
  WHERE 列名1 = @para1;

  RETURN 0

  --例外処理
  ERR:
    RETURN 1

END

GO

 

処理の前半では、ストアドなので、テーブルの更新やら追加やらなんやらがある想定です。
その後の処理として、11行目で「一時テーブル」を作成しています。

SQL Serverの場合は、テーブル名の先頭に # を付けると一時テーブルとして扱われます。

# が一つだと「ローカル一時テーブル」となり、作成したユーザーのみが参照できます。
# が二つだと「グローバル一時テーブル」となり、作成したユーザー以外でも参照できます。

一時テーブルは、使用していたセッションが切断されると消滅しますが、グローバル一時テーブルの場合は複数のユーザーのセッションして使用されていた場合はそのすべてのユーザーのセッションが切断されてから消滅します。
※本来はローカル一時テーブルを使用するのが望ましいのですが、当方で検証していた結果、ローカル一時テーブルだとVBA側から上手く見つけられなかったため、グローバル一時テーブルを使用しています。ここは時間があれば調べたいところです・・・。

20行目で一時テーブルに対して特定のテーブルのSELECT結果をINSERTしています。
ここでINSERTした一時テーブル内のデータは、このセッションが生きている間は残存します。
 

VBA側のサンプルコード

実行すると、ストアドを呼び出して、ストアド内で作成した一時テーブルを参照します。
その参照結果をレコードセットに格納して、debug.printで出力します。

Sub Sample()

Dim db As test_project.DataBaseAccess
Dim Rs As ADODB.Recordset
Dim strSQL As String
Dim strSpName As String
Dim strPara(0, 2) As String	'必要により添え字を変更
Dim varReturn As Variant

    Set db = test_project.DatabaseFactory.creater

    'サーバデータベースと接続します。
    db.connect

    strSpName = "dbo.uspSelectTest"

    'パラメーター1
    strPara(0, 0) = "para1"	'パラメーター名
    strPara(0, 1) = adBigInt	'データ型
    strPara(0, 2) = 1111	'値
    'パラメーター2 ※必要によってコメントを外して使用してください。
    'strPara(1, 0) = "para2"	'パラメーター名
    'strPara(1, 1) = adBigInt	'データ型
    'strPara(1, 2) = 0		'値

   'パラメーターを渡してストアドを実行します。
    varReturn = db.execute_usp(strSpName, strPara)

    'ストアドの戻り値が0(正常終了)か否かを判定します。
    If varReturn = 0 Then
    
    	'ストアドが作成した一時テーブルをSELECTします。
        strSQL = "SELECT * FROM ##tempテーブル"
        Set Rs = db.execute(strSQL)
        'レコードセットの行数文ループします。
        Do Until Rs.EOF
            Debug.Print Rs(0).Value & " | " & Rs(1).Value & " | " & Rs(2).Value & " | " & Rs(3).Value & " | " & Rs(4).Value
            Rs.MoveNext
        Loop
        
        Set Rs = Nothing
    End If
    
    db.disconnect
    Set db = Nothing

End Sub

3行目と10行目のプロジェクト名は実際に使用するVBAのプロジェクト名に書き変えてください。
27行目で使用しているメソッドは、以前の記事で紹介しているストアド実行用のメソッドです。
33行目からは一時テーブルを指定してSELECT文を実行してその結果をレコードセットに格納しています。
 
 

最後に

当記事は、当ブログへの問い合わせを元に実際に検証して記事に致しました。
いったんはストアドのSELECT結果をVBAで受け取れないと記事を書いてしまい、再度検証してみたら普通に出来てしまって、慌てて記事を修正させていただきました。

今後は間違った内容の記事を公開してしまわないように気を付けます。
結果的には二通りの実装方法を紹介できたのでヨシとします。

今回も読んで頂きましてありがとうございました。
それでは皆さまごきげんよう!

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