2012-06-29 7 views
5

Potrzebuję pomocy w zrozumieniu, jeśli sposób, w jaki próbuję użyć Ref Cursor jako parametru ReturnValue dla wielu rekordów/wartości, przy czym PL/SQL jest właśnie tekstem CommandText obiektu OracleCommand, a nie w zapisanej procedurze lub funkcji, jest nawet możliwe.Jak używać Oracle Ref Cursor z C# ODP.NET jako parametru ReturnValue, bez użycia zapisanej funkcji lub procedury?

Jeśli nie jest to możliwe, staram się znaleźć sposób na wydanie instrukcji PL/SQL, która zaktualizuje nieznaną liczbę rekordów (w zależności od tego, ile z nich zgadza się z klauzulą ​​WHERE) i zwraca Identyfikatory wszystkich rekordów Zaktualizowane w OracleDataReader, przy użyciu pojedynczego obiegu do bazy danych, bez użycia procedury składowanej lub funkcji.

Pracuję z Oracle 11g przy użyciu ODP.NET do komunikacji z istniejącą bazą kodową C# .NET 4.0, która używa połączenia SQL do pobierania/modyfikowania danych. Uproszczona definicja tabeli testu używam wygląda tak:

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

ja wstępnie wypełnić tabelę z trzech wartości testowych tak:

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; 

Istniejąca instrukcja SQL, ładowane z pliku skryptu nazwany Oracle_UpdateWorkerStatus2, a zawarte w OracleCommand.CommandText wygląda tak:

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; 

stworzyłem mały program w C# test próbować izolować, gdzie jestem coraz o RA-01036 „nielegalne zmiennej Nazwa/numer” błąd, który ma główny korpus, który wygląda tak:

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

próbowałam zmianę nazwy parametrów, nazywania i nie nazewnictwa parametr UpdatedRecords, zmieniając kolejność tak Zaktualizowane zapisy są pierwsze lub ostatnie. Najbliższą rzeczą, którą znalazłem do tej pory jest następujące pytanie StackOverflow (How to call an Oracle function with a Ref Cursor as Out-parameter from C#?), ale nadal używa funkcji przechowywanej, o ile mogę powiedzieć.

Uruchamianie skryptu/SQL Oracle_UpdateWorkerStatus2 PL z SQL Developer, to otwiera okno „Enter wiąże” gdzie mogę wprowadzić wartości CurentStateId, StateId i StateReasonId jak w powyższym kodzie, ale daje następujący raport o błędzie:

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: 

Nie do końca rozumiem, dlaczego mówi mi, że tabela nie istnieje, kiedy zdefiniowałem tabelę WorkerStatus i zadeklarowałem zmienną t_Ids, typu id_array, również na tabelę. Każda pomoc tutaj jest bardzo doceniana.

+0

Czy może to być problem z uprawnieniami/widocznością? Po ręcznym połączeniu się z kontem Oracle, na którym wykonywałeś skrypt "Oracle_UpdateWorkerStatus2", możesz wybrać z tabeli status_umenu? –

+0

Jestem w stanie wybrać z tabeli WorkerStatus, a także upuścić i utworzyć go. Jestem całkiem nowy w firmie Oracle, więc nie wiem, czy są jakieś specjalne przywileje, których potrzebowałbym, aby móc korzystać z kursorów ref., Ale nasz technik IT/DBA ustawił mnie z wszystkimi przywilejami zwykle używanymi przez naszych programistów, więc nie uważam, że jest to kwestia uprzywilejowana. Poproszę go jednak w poniedziałek. – Paul

+0

Sprawdzone z naszym DBA, i nie jest on świadomy żadnych specjalnych uprawnień wymaganych do robienia tego, co próbuję. Zrobił też szybki skan tego, co mam tutaj, i nie widzi żadnego powodu, dla którego nie działa. Jakieś przemyślenia na temat tego, co robię źle, lub inny sposób osiągnięcia celów? – Paul

Odpowiedz

5

Spróbuję odpowiedzi zamiast innego komentarza.

Jak powiedziałem w jednym komentarzu, czysta/prosta instrukcja select nie działa w PL/SQL. Ale myliłem się twierdząc, że potrzebujesz funkcji zapisanej, aby zwrócić kursor ref.

Ale przede wszystkim: Typ "id_array", który deklarujesz w swoim bloku PL/SQL, to typ PL/SQL. Nie można go użyć w instrukcji wyboru kursora ref. Zamiast tego będziesz potrzebować typu SQL:

create type id_array as table of number; 

To musi być wykonane tylko raz, tak jak "utwórz stół".

Twój PL/SQL blok mógłby wyglądać następująco:

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:
Podczas montażu ten post, zdałem sobie sprawę, gdzie ORA-00942 może pochodzić. Tablica t_ids została oparta na typie PL/SQL, który nie jest znany/dostępny po stronie SQL.

+0

Dziękuję. Działa to doskonale! – Paul

+0

Btw, dla każdego, kto patrzy na to później, edytowałem kod C# powyżej, aby pracować z: rcursor, zamiast: ReturnValue. Aby uzyskać przykład miejsca pracy, kod C# do nowej aplikacji konsoli, zamień mój PL/SQL z pliku skryptu Oracle_UpdateWorkerStatus2 na blok PL/SQL Juergen. Wykonaj CREATE TYPE raz, jak wspomina Juergen. Następnie uruchom aplikację C# Console, która powinna zawierać listę trzech identyfikatorów rekordów. – Paul

Powiązane problemy