Database Support Blog

  • EDB
2016.03.08

EDB Postgresを.NET Frameworkから使ってみよう(プロシージャの実行)

前回はEDB Postgresの.NET Framework用の接続ドライバであるEDBDataProviderを使用してバインド変数付のSQLを実行する方法を紹介しました。今回は、PL/SQLで作成したストアドプロシージャを実行する方法を紹介します。

環境

本手順は以下環境で実施しています。

実行環境

## Database
* OS: OracleLinux 6.4 64bit
* DB: EPAS 9.6.2 (64bit)
## Client
* OS: Windows 7
* EDB Connectors 9.6.2
* .NET Framework 4.5
* VisualStudio 2017 Community Editon

参照ライブラリについて

EDB Postgres 9.6では.NET Frameworkのバージョンに合わせて以下の2種類のライブラリが提供されています。
 
* .NET Framework 4.0
 - 4.0/EDBDataProvider2.0.2.dll
* .NET Framework 4.5-
 - 4.5/EnterpriseDB.Client.dll
 
本来はこのバージョンに則って参照に追加すべきですが、9.6.2でのEnterpriseDB.Client.dllにはOUTパラメータに関して正常に動作しない不具合が確認されています。
 
そのため、.NET Framework 4.5以降であってもEDBDataProvider2.0.2.dllをご利用ください。こちらの組み合わせ自体でもEnterprise DB社社から動作保証されております。

シンプルなプロシージャの実行

EDB Postgresの元になっているPostgreSQLにもPL/pgSQLといった手続き型のプロシージャ言語がありますが、EDB PostgresではOracle互換のPL/SQLを使用することができますので、こちらを使用したストアドプロシージャを呼び出してみます。
まずは、INパラメータとOUTパラメータが1つずつのプロシージャを実行してみます。以下は、第一引数に指定されたパラメータを2倍にして第二引数のOUTパラメータに代入するプロシージャです。

サンプルプロシージャ1

create or replace procedure double_proc(v_src number, v_dest out number)
is
begin
v_dest := v_src * 2;
end;

このプロシージャをEDBDataProviderから呼び出す場合は以下の様なソースになります。

Dim command As EDBCommand = New EDBCommand("double_proc(:in_param, :out_param)", conn)
command.CommandType = CommandType.StoredProcedure
command.Parameters.Add(
    New EDBParameter(
        "in_param",
        EDBTypes.EDBDbType.Integer,
        100,
        "in_param",
        ParameterDirection.Input, False, 2, 2,
        System.Data.DataRowVersion.Current,
        10
    )
)
command.Parameters.Add(
    New EDBParameter(
        "out_param",
        EDBTypes.EDBDbType.Integer,
        100,
        "out_param",
        ParameterDirection.Output, False, 2, 2,
        System.Data.DataRowVersion.Current,
        1
    )
)
' prepareを実行
command.Prepare()
' INパラメータに10を指定し実行
command.Parameters("in_param").Value = 10
command.ExecuteNonQuery()
' OUTパラメータには10*2の20が代入されている
Console.WriteLine(command.Parameters("out_param").Value)

プロシージャの引数をバインド変数にする場合、IN OUTといった変数の向きを指定する必要があります。バインド変数の向きはEDBCommandの第5引数でParameterDirectionを用いて指定します。この場合、他の引数を省略できなくなるため、EDBCommandへ10個の引数を渡す必要が有ることに注意します。ParameterDirection以外の部分については以下のマニュアルを参考にしてください。
 
4.6.3 Example - Executing a Stored Procedure with IN, OUT, and INOUT Parameters

REF CURSORを使用するプロシージャ

続いて、REF CURSORを用いたプロシージャを実行してみます。以下はIN,OUTのパラメータを一つずつ持ち、INパラメータで指定されたDEPTNOに所属する社員名一覧のカーソルを戻すプロシージャです。

サンプルプロシージャ2

create or replace procedure refcur_proc(v_deptno number, v_result out sys_refcursor)
is
begin
open v_result for select ename from emp where deptno = v_deptno;
end;

このプロシージャをEDBDataProviderから呼び出す場合は以下の様なソースになります。

Dim tran As EDBTransaction = conn.BeginTransaction() ' point1
Dim command As EDBCommand = New EDBCommand("refcur_proc(:in_param, :out_param)", conn)
command.CommandType = CommandType.StoredProcedure
command.Parameters.Add(
    New EDBParameter(
        "in_param",
        EDBTypes.EDBDbType.Integer,
        100,
        "in_param",
        ParameterDirection.Input, False, 2, 2,
        System.Data.DataRowVersion.Current,
        10
    )
)
command.Parameters.Add(
    New EDBParameter(
        "out_param",
        EDBTypes.EDBDbType.RefCursor, ' point2
        1000,
        "out_param",
        ParameterDirection.Output, False, 2, 2,
        System.Data.DataRowVersion.Current,
        DBNull.Value
    )
)
command.Prepare()
command.Parameters("in_param").Value = 10
command.ExecuteNonQuery()
Dim reader As EDBDataReader = command.Parameters("out_param").Value ' point3
:
reader.Close()
tran.Rollback()

大きなポイントが3箇所あります。

トランザクションが必須(point1)

REF CURSORを使用する場合、カーソルの取得から使用までが同じトランザクションである必要があります。BeginTransactionを実行しない場合は自動コミットですので、プロシージャを実行した時点でCOMMITが行われます。その時点で取得したカーソルがクローズされてしまうため、結果を取得することができなくなってしまいます。
 
実際に、トランザクションを開始せずにREF CURSORを取得すると、カーソルからフェッチしようとした時点で以下の様なエラーを受けます。

EnterpriseDB.EDBClient.EDBException:
cursor "unnamed portal 1" does not exist

これは、フェッチしようとしたカーソルが既に存在しないことを示しています。この様にREF CURSORを使用する場合はトランザクションを必ず使用します。

EDBTypes.EDBDbType.RefCursorの指定(point2)

REF CURSORを受け取る場合、EDBDataProvider側の型指定はEDBTypes.EDBDbType.RefCursorとします。これにより、OUTパラメータにはEDBDataProviderが適宜フェッチした結果が代入され、DataReaderとして使用できる形に変換してくれます。

EDBDataReaderとしての結果の取り出し(point3)

ExecuteNonQueryでプロシージャを実行した時点で、out_paramにはカーソルをフェッチした結果が代入されています。この際の.NET Framework側でのクラスはEnterpriseDB.EDBClient.CachingDataReaderというDataReader互換のものになっています。そのため、DataReaderとして受け取ることで通常のSELECT文と同じように扱うことが可能です。

まとめ

ストアドプロシージャをEDBDataProviderから実行する方法を紹介しました。EDB PostgresではOracle互換のPL/SQLが使えるため、その点について興味を持たれる方も多いかと思います。特にREF CURSORについては呼び出し方に対象の癖や注意点がありますので、今回の記事がそのような方の一助となりましたら幸いです。

筆者情報

山田 聡 画像

2011年にアシストに入社してからサポートセンターでOracle DatabaseやPostgreSQL、EDB Postgres Advanced Server(EPAS)といったデータベース製品を中心に担当しています。個人的にJavaやNode.js、Python等を触っていますので、PostgreSQLと絡めてアプリケーション周りの情報をお送りしていきます。


■本記事の内容について
 本記事に記載されている製品およびサービス、定義及び条件は、特段の記載のない限り本記事執筆時点のものであり、予告なく変更になる可能性があります。あらかじめご了承ください。

■商標に関して
 ・Oracle®、Java、MySQL及びNetSuiteは、Oracle、その子会社及び関連会社の米国及びその他の国における登録商標です。
 ・Amazon Web Services、AWS、Powered by AWS ロゴ、[およびかかる資料で使用されるその他の AWS 商標] は、Amazon.com, Inc. またはその関連会社の商標です。
  文中の社名、商品名等は各社の商標または登録商標である場合があります。

関連している記事

  • EDB
  • PostgreSQL
2025.11.19

今さら聞けない!?WALアーカイブのベストプラクティス

PostgreSQL開発に多く貢献しているEnterpriseDB社による WALアーカイブ設定に関するベストプラクティスをご紹介します。

  • EDB
  • PostgreSQL
2025.04.16

PostgreSQLの拡張機能「system_stats」のご紹介

EDB社が提供するPostgreSQLの拡張機能「system_stats」はPostgreSQL ユーザーがパフォーマンス問題に取り組む際の非常に強力なツールになります。SQLクエリでOS情報を取得できるため、DBエンジニアにとってはパフォーマンスの監視が格段に簡単になります。テストした結果をご紹介します。

  • EDB
  • PostgreSQL
2025.03.10

意外な落とし穴!アプリケーション⇒DBデータ型によるパフォーマンス影響

PostgreSQLのオプティマイザがインデックスを適切に使用できない理由は様々ですが、本記事ではJDBC⇔PostgreSQL間でデータ型の不一致がインデックスの使用にどういった悪影響を及ぼすかを見ていきます

ページの先頭へ戻る