5

PL/SQL が OracleCommand オブジェクトの CommandText であり、ストアド プロシージャまたは関数ではなく、複数のレコード/値の ReturnValue パラメータとして Ref Cursor を使用しようとしている方法が偶数であるかどうかを理解するのに助けが必要です可能。

それが不可能な場合、私がしようとしているのは、不明な数のレコードを更新する PL/SQL ステートメントを発行する方法を見つけることです (WHERE 句に一致するレコードの数によって異なります)。ストアド プロシージャや関数を使用せずに、データベースへの 1 回のラウンドトリップを使用して、OracleDataReader で更新されます。

SQL接続を使用してデータを取得/変更する既存のC#.NET 4.0コードベースとの通信にODP.NETを使用してOracle 11gを使用しています。私が使用している簡略化されたテスト テーブル定義は次のようになります。

CREATE TABLE WorkerStatus
(
    Id                  NUMERIC(38)         NOT NULL
    ,StateId            NUMERIC(38)         NOT NULL
    ,StateReasonId      NUMERIC(38)         NOT NULL
    ,CONSTRAINT PK_WorkerStatus PRIMARY KEY ( Id )
)

次のように、テーブルに 3 つのテスト値を事前に入力します。

BEGIN
    EXECUTE IMMEDIATE 'INSERT INTO WorkerStatus (Id, StateId, StateReasonId)
                        VALUES (1, 0, 0)';
    EXECUTE IMMEDIATE 'INSERT INTO WorkerStatus (Id, StateId, StateReasonId)
                        VALUES (2, 0, 0)';
    EXECUTE IMMEDIATE 'INSERT INTO WorkerStatus (Id, StateId, StateReasonId)
                        VALUES (3, 0, 0)';
END;

Oracle_UpdateWorkerStatus2 という名前のスクリプト ファイルからロードされ、OracleCommand.CommandText に含まれる既存の SQL ステートメントは次のようになります。

DECLARE
    TYPE id_array IS TABLE OF WorkerStatus.Id%TYPE INDEX BY PLS_INTEGER;    

    t_ids   id_array;
BEGIN
    UPDATE WorkerStatus
    SET
         StateId = :StateId
        ,StateReasonId = :StateReasonId
    WHERE
        StateId = :CurrentStateId
    RETURNING Id BULK COLLECT INTO t_Ids;
    SELECT Id FROM t_Ids;
END;

ORA-01036「不正な変数名/番号」エラーが発生している場所を特定するために、小さな C# テスト プログラムを作成しました。本体は次のようになります。

using System;
using System.Configuration;
using System.Data;
using System.Text;
using Oracle.DataAccess.Client;
using Oracle.DataAccess.Types;
namespace OracleDbTest
{
  class Program
  {
    static void Main(string[] args)
    {
        // Load the SQL command from the script file.
        StringBuilder sql = new StringBuilder();
        sql.Append(Properties.Resources.Oracle_UpdateWorkerStatus2);

        // Build and excute the command.
        OracleConnection cn = new OracleConnection(ConfigurationManager.ConnectionStrings["OracleSystemConnection"].ConnectionString);
        using (OracleCommand cmd = new OracleCommand(sql.ToString(), cn))
        {
            cmd.BindByName = true;
            cn.Open();

            OracleParameter UpdatedRecords  = new OracleParameter();
            UpdatedRecords.OracleDbType     = OracleDbType.RefCursor;
            UpdatedRecords.Direction        = ParameterDirection.ReturnValue;
            UpdatedRecords.ParameterName    = "rcursor";

            OracleParameter StateId         = new OracleParameter();
            StateId.OracleDbType            = OracleDbType.Int32;
            StateId.Value                   = 1;
            StateId.ParameterName           = "StateId";

            OracleParameter StateReasonId   = new OracleParameter();
            StateReasonId.OracleDbType      = OracleDbType.Int32;
            StateReasonId.Value             = 1;
            StateReasonId.ParameterName     = "StateReasonId";

            OracleParameter CurrentStateId  = new OracleParameter();
            CurrentStateId.OracleDbType     = OracleDbType.Int32;
            CurrentStateId.Value            = 0;
            CurrentStateId.ParameterName    = "CurrentStateId";

            cmd.Parameters.Add(UpdatedRecords);
            cmd.Parameters.Add(StateId);
            cmd.Parameters.Add(StateReasonId);
            cmd.Parameters.Add(CurrentStateId);

            try
            {
                cmd.ExecuteNonQuery();
                OracleDataReader dr = ((OracleRefCursor)UpdatedRecords.Value).GetDataReader();
                while (dr.Read())
                {
                    Console.WriteLine("{0} affected.", dr.GetValue(0));
                }
                dr.Close();
            }
            catch (OracleException e)
            {
                foreach (OracleError err in e.Errors)
                {
                    Console.WriteLine("Message:\n{0}\nSource:\n{1}\n", err.Message, err.Source);
                    System.Diagnostics.Debug.WriteLine("Message:\n{0}\nSource:\n{1}\n", err.Message, err.Source);
                }
            }
            cn.Close();
        }
        Console.WriteLine("Press Any Key To Exit.\n");
        Console.ReadKey(false);
    }
  }
}

パラメータ名の変更、UpdatedRecords パラメータの命名と非命名、順序を変更して UpdatedRecords が最初または最後になるようにしました。私がこれまでに見つけた最も近いものは、次の StackOverflow の質問 ( C# から Out-parameter として Ref Cursor を使用して Oracle 関数を呼び出す方法は? ) ですが、私が知る限り、それでもストアド関数を使用しています。

Oracle_UpdateWorkerStatus2 PL/SQLスクリプトをSQL Developerから実行すると、「Enter Binds」ダイアログが開き、上記のコードのようにCurentStateId、StateIdおよびStateReasonIdの値を入力しますが、次のエラー・レポートが表示されます:

Error report:
ORA-06550: line 13, column 17:
PL/SQL: ORA-00942: table or view does not exist
ORA-06550: line 13, column 2:
PL/SQL: SQL Statement ignored
06550. 00000 -  "line %s, column %s:\n%s"
*Cause:    Usually a PL/SQL compilation error.
*Action:

WorkerStatus テーブルを定義し、id_array 型の t_Ids 変数もテーブルとして宣言したときに、テーブルが存在しないと表示される理由がよくわかりません。ここで何か助けていただければ幸いです。

4

1 に答える 1

5

別のコメントの代わりに答えを試してみます。

あるコメントで述べたように、純粋/単純なselectステートメントはPL/SQLでは機能しません。しかし、refカーソルを返すにはストアド関数が必要だと言ったのは間違っていました。

ただし、まず最初に、PL/SQLブロックで宣言するタイプ「id_array」はPL/SQLタイプです。refカーソルselectステートメントでは使用できません。代わりに、SQLタイプが必要になります。

create type id_array as table of number;

これは、「テーブルの作成」と同じように、一度だけ実行する必要があります。

PL/SQLブロックは次のようになります。

DECLARE
    t_ids   id_array;
BEGIN
    UPDATE WorkerStatus
    SET
         StateId = :StateId
        ,StateReasonId = :StateReasonId
    WHERE
        StateId = :CurrentStateId
    RETURNING Id BULK COLLECT INTO t_Ids;

    OPEN :rcursor FOR SELECT * FROM TABLE(cast(t_Ids as id_array));    
END;

PS:
この投稿を組み立てているときに、ORA-00942がどこから来たのかを理解しました。配列t_idsはPL/SQLタイプに基づいていましたが、SQL側では不明/使用可能ではありません。

于 2012-07-03T00:39:35.293 に答える