2012-06-29 2 views
5

PL/SQL이 OracleCommand 객체의 CommandText이고 저장 프로 시저 또는 함수가 아닌 여러 레코드/값에 대해 RefValue 매개 변수로 Ref Cursor를 사용하려는 경우 이해가 필요합니다. 심지어 가능합니다.저장된 함수 또는 프로 시저를 사용하지 않고 C# ODP.NET의 Oracle Ref Cursor를 ReturnValue 매개 변수로 사용하는 방법?

그럴 수 없다면, 내가하려는 것은 WHERE 절과 일치하는 일치하는 레코드 수를 알 수없는 PL/SQL 문을 발행하는 방법을 찾아서 모든 레코드의 ID 저장 프로 시저 또는 함수를 사용하지 않고 데이터베이스에 대한 단일 라운드 트립을 사용하여 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) 
) 

난과 같이 세 가지 테스트 값으로 테이블을 채울 사전 :

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; 

기존의 SQL 문, 스크립트 파일에서로드 나는 오지고있어 어디 분리하려고 시도하는 작은 C# 테스트 프로그램을 만들었습니다

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; 

:과 같이 Oracle_UpdateWorkerStatus2 이름과 OracleCommand.CommandText에 포함 보인다 그래서 순서를 변경, 내가 이름과 UpdatedRecords 매개 변수를 명명하지, 매개 변수 이름을 변경하려고했습니다

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); 
    } 
    } 
} 

: 같은 보이는 본체를 가지고 RA-01036 "불법 변수 이름/번호"오류 UpdatedRecords는 처음 또는 마지막입니다. 지금까지 찾은 가장 가까운 것은 다음 StackOverflow 질문 (How to call an Oracle function with a Ref Cursor as Out-parameter from C#?)이지만 여전히 내가 말할 수있는 한 저장 함수를 사용합니다. SQL Developer에서 Oracle_UpdateWorkerStatus2 PL/SQL 스크립트를 실행

는, 내가 위의 코드와 같이 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 변수를 테이블로 선언했을 때 실제로 테이블이 존재하지 않는다고 말하는 이유를 알 수 없습니다. 여기에 도움을 주시면 대단히 감사하겠습니다.

+0

권한/가시성 문제 일 수 있습니까? 스크립트 "Oracle_UpdateWorkerStatus2"를 실행중인 oracle 계정에 수동으로 연결할 때 workerstatus 테이블에서 선택할 수 있습니까? –

+0

WorkerStatus 테이블에서 선택 및 삭제할 수 있습니다. 필자는 오라클에게 상당히 익숙하므로 ref 커서를 사용할 수 있어야하는 특별한 권한이 있는지는 모르지만 IT/DBA 기술자는 개발자가 일반적으로 사용하는 모든 권한을 설정해 주지만, 그래서 나는 이것이 특권의 문제라고 생각하지 않는다. 나는 그에게 월요일에 물을 것이다. – Paul

+0

우리 DBA에 확인해 봤는데, 그는 내가하고있는 일을하기 위해 요구되는 특별한 특권을 알지 못합니다. 또한, 그는 내가 여기에있는 것을 빠르게 검사했고 그것이 작동하지 않는 이유를 보지 못했습니다. 내가 잘못하고있는 일에 대한 생각이나 목표를 달성하기위한 또 다른 방법? – Paul

답변

5

다른 의견 대신 답변을 시도합니다.

하나의 주석에서 언급했듯이 순수/단순 select 문은 PL/SQL에서 작동하지 않습니다. 하지만 내가 진술서에 잘못했다. 참조 커서를 반환하기 위해 저장된 함수가 필요하다.

첫 번째 사항 : PL/SQL 블록에서 선언 한 유형 "id_array"는 PL/SQL 유형입니다. 참조 커서 선택 문에는 사용할 수 없습니다. 대신에 다음과 같은 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는 SQL 측에서 알 수 없거나 사용할 수없는 PL/SQL 유형을 기반으로했습니다.

+0

감사합니다. 이것은 완벽하게 작동합니다! – Paul

+0

Btw, 나중에 보자면 rcursor 대신 ReturnValue를 사용하여 위의 C# 코드를 편집했습니다. C# 코드를 새 콘솔 애플리케이션에 배치하는 예제를 얻으려면 Oracle_UpdateWorkerStatus2 스크립트 파일의 내 PL/SQL을 Juergen의 PL/SQL 블록으로 대체하십시오. Juergen이 언급 할 때 CREATE TYPE을 한 번 실행하십시오. 그런 다음 C# Console Application을 실행하면 영향을받는 레코드의 3 개의 ID가 나열되어야합니다. – Paul

관련 문제