ストアドプロシージャの結果を取得する。
スクリプトからストアドプロシージャの結果を取得する。
MSDEでは(SQL Server 7.0も)ストアドファンクション、いわゆるユーザ定義関数はない。
SQL Server 2000 ではサポートされたらしいけど。
ストアドプロシージャから値を取得する方法は3通りある。
1つ目はストアドプロシージャの返り値を取得する方法。
ストアドプロシージャは整数型限定だが値を返すことができるのでこれを利用する。
2つ目はストアドプロシージャの結果セットを使う方法。
ストアドプロシージャ内でSELECT文を実行するとその結果を
ストアドプロシージャの結果セットとして取得することができる。
3つ目はストアドプロシージャが実際のテーブルの値を変化させて、その結果をあとから取得する方法。
この3つめの方法は自明かつあたりまえなのでここでは試してみない。
●ストアドプロシージャの返り値を取得する
まず、ストアドプロシージャの返り値をスクリプトから取得しよう。
ストアドプロシージャのテストをしたときにGetAuthorAge というプロシージャを作ったので
これを使ってみることにする。
一応再掲しておこう。
1> CREATE PROCEDURE GetAuthorAge (@BookID INTEGER ) AS
2> DECLARE @AuthorAge INTEGER
3> SET @AuthorAge = -1
4> IF ( SELECT COUNT(*) FROM BookTable WHERE BookID=@BookID ) > 0
5> SELECT @AuthorAge = AuthorTable.Age From AuthorTable, BookTable
6> WHERE BookTable.BookID = @BookID
7> RETURN @AuthorAge
8> GO
こんな感じだった。
実行するときはこんな感じ。
1> DECLARE @Result INTEGER
2> EXECUTE @Result = GetAuthorAge 3
3> PRINT @Result
4> GO
10
1>
さて、ここでは値をスクリプト内で取得しなければいけない。
PRINT文を飛ばしても意味がないので、SELECT文を使う。
T-SQLでは文区切りがないので、複数文をひとつのSQL文にして飛ばすときには、
複数文を続けて書くという恐ろしいことをする。
実行するSQL文はこんな感じになる。
"DECLARE @Result INTEGER EXECUTE @Result = GetAuthorAge 3 SELECT @Result"
というわけでスクリプトはこんな感じ。
MSDE-S02.vbs
' ADODB を使ったサンプル
' Stdoutを使うので、CScriptで実行すること
' ストアドプロシージャの返り値を取得する。
Dim objConn, objRS
set objConn = WScript.CreateObject("ADODB.Connection")
objConn.Open ( "Provider=SQLOLEDB.1;Password=kurokuro;User ID=kuro;Initial Catalog=KuroDB" )
set objRS = objConn.Execute( _
"DECLARE @Result INTEGER EXECUTE @Result = GetAuthorAge 3 SELECT @Result" )
If objRS.EOF Then
Wsh.Stdout.Write "No Result" & vbCRLF
Else
Wsh.Stdout.Write "Result = " & objRS(0) & vbCRLF
End If
実行結果は以下のようになる。結果の「10」が取得できている。
C:\Temp>cscript -nologo msde-s02.vbs
Result = 10
C:\Temp>
●結果セットを使う。
次に結果セットを使ってストアドプロシージャの結果を取得してみよう。
ストアドプロシージャの中でSELECT文を使うと、その結果を呼び出し側から取得できる。
とりあえず、AuthorTableの中身を全部取得するプロシージャを作ってみよう。
1> CREATE PROCEDURE GetAllAuthors AS
2> SELECT * FROM AuthorTable
3> GO
これを使ったスクリプトは以下のようになる。
普通にSELECT文を実行したときと同じ扱いでいい。
スクリプトそのものは、MSDE-S01.vbsをちょっと改良しただけ。
MSDE-S03.vbs
' ADODB を使ったサンプル
' Stdoutを使うので、CScriptで実行すること
' ストアドプロシージャの結果セットを使う。
Dim objConn, objRS
set objConn = WScript.CreateObject("ADODB.Connection")
objConn.Open ( "Provider=SQLOLEDB.1;Password=kurokuro;User ID=kuro;Initial Catalog=KuroDB" )
set objRS = objConn.Execute( "EXEC GetAllAuthors" )
Wsh.Stdout.Write "* Fields of ResultSet * " & vbCRLF
For i=0 to objRS.Fields.Count -1
Wsh.Stdout.Write "Field(" & i & "): "& objRS.Fields(i).name & vbCRLF
Next
Wsh.Stdout.Write vbCRLF
Wsh.Stdout.Write "* Data in ResultSet * " & vbCRLF
Do While Not objRS.EOF
For i=0 to objRS.Fields.Count -1
Wsh.Stdout.Write objRS.Fields(i).name & ":"
Wsh.Stdout.Write objRS(i) & " "
Next
Wsh.Stdout.Write vbCRLF
objRS.MoveNext
Loop
実行してみる。
C:\Temp>cscript -nologo msde-s03.vbs
* Fields of ResultSet *
Field(0): AuthID
Field(1): Name
Field(2): Age
* Data in ResultSet *
AuthID:101 Name:太郎さん Age:100
AuthID:102 Name:次郎さん Age:50
AuthID:103 Name:三郎さん Age:10
C:\Temp>
おお。動いた動いた。
●複数の結果セットを使う。
結果セットは1つではなく複数持つことができるらしい。
つまりストアドプロシージャの中で複数回SELECT文を使うと、その結果を全部呼び出し側から取得できる。
次のレコードセットを取得するには、レコードセットオブジェクトの、NextRecordsetメソッドを使う。
MoveNextは、「レコードをひとつ進める」のに対して、
NextRecordsetは「次のレコードセットを取得する」ということに注意。
こんどは、AuthorTableとBookTableの中身を全部取得するプロシージャを作ってみよう。
1> CREATE PROCEDURE GetAllAuthorsAndBooks AS
2> SELECT * FROM AuthorTable
3> SELECT * FROM BookTable
4> GO
これを使ったスクリプトは以下のようになる。
さっきの、MSDE-S03.vbsをちょっと改良してループを入れただけ。
MSDE-S04.vbs
' ADODB を使ったサンプル
' Stdoutを使うので、CScriptで実行すること
' ストアドプロシージャの結果セットを使う。
Dim objConn, objRS
Set objConn = WScript.CreateObject("ADODB.Connection")
objConn.Open ( "Provider=SQLOLEDB.1;Password=kurokuro;User ID=kuro;Initial Catalog=KuroDB" )
Set objRS = objConn.Execute( "EXEC GetAllAuthorsAndBooks" )
Do While Not objRS Is Nothing
Wsh.Stdout.Write "**** Record Set Found ****" & vbCRLF
Wsh.Stdout.Write "* Fields of ResultSet * " & vbCRLF
For i=0 to objRS.Fields.Count -1
Wsh.Stdout.Write "Field(" & i & "): "& objRS.Fields(i).name & vbCRLF
Next
Wsh.Stdout.Write vbCRLF
Wsh.Stdout.Write "* Data in ResultSet * " & vbCRLF
Do While Not objRS.EOF
For i=0 to objRS.Fields.Count -1
Wsh.Stdout.Write objRS.Fields(i).name & ":"
Wsh.Stdout.Write objRS(i) & " "
Next
Wsh.Stdout.Write vbCRLF
objRS.MoveNext
Loop
Wsh.Stdout.Write vbCRLF
Set objRS = objRS.NextRecordSet()
Loop
実行してみる。
C:\Temp>cscript -nologo msde-s04.vbs
**** Record Set Found ****
* Fields of ResultSet *
Field(0): AuthID
Field(1): Name
Field(2): Age
* Data in ResultSet *
AuthID:101 Name:太郎さん Age:100
AuthID:102 Name:次郎さん Age:50
AuthID:103 Name:三郎さん Age:10
**** Record Set Found ****
* Fields of ResultSet *
Field(0): BookID
Field(1): Title
Field(2): AuthID
Field(3): Price
* Data in ResultSet *
BookID:1 Title:本その1 AuthID:102 Price:340
BookID:2 Title:本その2 AuthID:101 Price:10
BookID:3 Title:本その3 AuthID:103 Price:840
BookID:4 Title:Book No.Four AuthID:101 Price:9840
C:\Temp>
おお。動いた動いた。
このNextRecordsetメソッドでは、もとのSQL文が複合ステートメントだったときには、
NextRecordSet実行時点でステートメントが実行されるとか、
リモートサーバからマーシャリングがすんでいないときにはレコードセットが取得できないとか、
いろいろ複雑なことがからんでいるようだが、ここではこれでよしとしよう。
●システムプロシージャの結果の取得
ここでやったやり方はそのままシステムプロシージャの結果取得に使えるはずだ。
さっきの、MSDE-S04.VBSのSQLを実行するところを、
Set objRS = objConn.Execute( "sp_helprole" )
に変えて実行してみよう。
C:\Temp>cscript -nologo msde-s04s.vbs
**** Record Set Found ****
* Fields of ResultSet *
Field(0): RoleName
Field(1): RoleId
Field(2): IsAppRole
* Data in ResultSet *
RoleName:public RoleId:0 IsAppRole:0
RoleName:db_owner RoleId:16384 IsAppRole:0
RoleName:db_accessadmin RoleId:16385 IsAppRole:0
RoleName:db_securityadmin RoleId:16386 IsAppRole:0
RoleName:db_ddladmin RoleId:16387 IsAppRole:0
RoleName:db_backupoperator RoleId:16389 IsAppRole:0
RoleName:db_datareader RoleId:16390 IsAppRole:0
RoleName:db_datawriter RoleId:16391 IsAppRole:0
RoleName:db_denydatareader RoleId:16392 IsAppRole:0
RoleName:db_denydatawriter RoleId:16393 IsAppRole:0
C:\Temp>
動いた、動いた。
実は、最初は、sp_helprolemember db_owner としてやったのだが、
結果セットの中のSIDフィールドの型がVARBINARYになっていて、
Wsh.Stdout.Write objRS(i)
で失敗してしまった。
バイナリ型のデータは表示するときにちょっと細工をする必要があるみたい。
MSDE 実験ページへ戻る。