2012-06-29 23 views
5

Tôi cần trợ giúp tìm hiểu xem cách tôi đang cố gắng sử dụng một con trỏ Ref là một tham số ReturnValue cho nhiều bản ghi/giá trị, với PL/SQL chỉ là CommandText của đối tượng OracleCommand chứ không phải trong một thủ tục hoặc hàm được lưu trữ, thậm chí có thể. Nếu điều đó là không thể, những gì tôi đang cố gắng làm là tìm cách phát hành một câu lệnh PL/SQL sẽ cập nhật một số lượng bản ghi chưa biết (phụ thuộc vào số lượng khớp với mệnh đề WHERE) và trả về Các id của tất cả các bản ghi Cập nhật trong một OracleDataReader, sử dụng một vòng chuyến đi duy nhất tới cơ sở dữ liệu, mà không sử dụng một thủ tục hoặc hàm được lưu trữ.Làm thế nào để sử dụng một Oracle Ref Cursor từ C# ODP.NET như một tham số ReturnValue, mà không cần sử dụng một chức năng được lưu trữ hoặc thủ tục?

Tôi đang làm việc với Oracle 11g sử dụng ODP.NET để liên lạc với cơ sở mã C# .NET 4.0 hiện có sử dụng kết nối SQL để truy xuất/sửa đổi dữ liệu. Định nghĩa đơn giản hóa bảng thử nghiệm Tôi đang sử dụng trông giống như vậy:

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

tôi Pre-cư bảng với ba giá trị thử nghiệm như vậy:

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; 

các câu lệnh SQL hiện có, nạp từ một tập tin script tên Oracle_UpdateWorkerStatus2, và chứa trong OracleCommand.CommandText trông giống như vậy:

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; 

tôi đã tạo ra một chương trình thử nghiệm # C nhỏ để cố gắng cô lập nơi tôi nhận được một O RA-01.036 "bất hợp pháp biến tên/số" lỗi mà có một cơ thể chính mà trông giống như vậy:

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

tôi đã cố gắng thay đổi tên tham số, đặt tên và không đặt tên các tham số UpdatedRecords, thay đổi thứ tự để UpdateRecords là lần đầu tiên hoặc cuối cùng. Điều gần nhất tôi đã tìm thấy cho đến nay là câu hỏi StackOverflow sau đây (How to call an Oracle function with a Ref Cursor as Out-parameter from C#?), nhưng vẫn sử dụng một chức năng được lưu trữ như xa như tôi có thể nói.

Chạy kịch bản Oracle_UpdateWorkerStatus2 PL/SQL từ SQL Developer, nó sẽ mở ra "Enter liên kết" hộp thoại nơi tôi nhập các giá trị cho CurentStateId, StateId và StateReasonId như trong đoạn mã trên, nhưng nó mang lại cho các báo cáo lỗi sau:

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: 

Tôi thực sự không hiểu tại sao nó cho tôi biết bảng không tồn tại, khi tôi đã định nghĩa bảng WorkerStatus và khai báo biến t_Ids, kiểu id_array, cũng là một bảng. Bất kỳ trợ giúp ở đây được đánh giá cao.

+0

Đây có phải là vấn đề về đặc quyền/khả năng hiển thị không? Khi bạn kết nối thủ công với tài khoản oracle, bạn đã thực thi kịch bản lệnh "Oracle_UpdateWorkerStatus2", bạn có thể chọn từ bảng workerstatus không? –

+0

Tôi có thể chọn từ bảng WorkerStatus, cũng như thả và tạo nó. Tôi khá mới với Oracle, vì vậy tôi không biết liệu có bất kỳ đặc quyền nào đặc biệt tôi cần để sử dụng các con trỏ không, nhưng kỹ thuật viên IT/DBA của chúng tôi đã thiết lập tất cả các đặc quyền mà các nhà phát triển của chúng tôi thường sử dụng, vì vậy tôi không tin đây là vấn đề đặc quyền. Tôi sẽ hỏi anh ta vào thứ hai mặc dù. – Paul

+0

Đã kiểm tra với DBA của chúng tôi và anh ấy không biết bất kỳ đặc quyền đặc biệt nào được yêu cầu để làm những gì tôi đang cố gắng. Ngoài ra, ông đã nhanh chóng quét những gì tôi đã có ở đây, và không thấy bất kỳ lý do tại sao nó không hoạt động. Bất kỳ suy nghĩ về những gì tôi có thể làm sai, hoặc một cách khác để đạt được các mục tiêu? – Paul

Trả lời

5

Tôi sẽ thử câu trả lời thay vì nhận xét khác.

Như tôi đã nói trong một nhận xét, một câu lệnh chọn thuần túy/đơn giản không hoạt động trong PL/SQL. Nhưng tôi đã sai khi nói, rằng bạn cần một hàm được lưu trữ để trả về một con trỏ ref.

Nhưng trước tiên, loại "id_array" bạn khai báo trong khối PL/SQL là kiểu PL/SQL. Nó không thể được sử dụng trong câu lệnh chọn con trỏ ref. Thay vào đó, bạn sẽ cần một loại SQL:

create type id_array as table of number; 

Điều này cần được thực hiện chỉ một lần, giống như "tạo bảng".

PL/SQL-block của bạn sau đó có thể trông như thế này:

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:
Trong khi lắp ráp bài này, tôi nhận ra nơi ORA-00.942 có thể đến từ. Các mảng t_ids được dựa trên một kiểu PL/SQL, không được biết/có sẵn ở phía SQL.

+0

Cảm ơn bạn. Điều này hoạt động hoàn hảo! – Paul

+0

Btw, đối với bất kỳ ai xem xét điều này sau, tôi đã chỉnh sửa mã C# ở trên để làm việc với: rcursor, thay vì: ReturnValue. Để lấy ví dụ làm việc, hãy đặt mã C# vào một ứng dụng Console mới, thay thế PL/SQL của tôi từ tệp kịch bản lệnh Oracle_UpdateWorkerStatus2 bằng khối PL/SQL của Juergen. Thực thi CREATE TYPE một lần như Juergen đề cập đến. Sau đó chạy ứng dụng C# Console và nó sẽ liệt kê 3 id của các bản ghi bị ảnh hưởng. – Paul