我需要帮助理解我是否尝试将Ref Cursor用作多个记录/值的ReturnValue参数,PL / SQL只是OracleCommand对象的CommandText而不是存储过程或函数,甚至可能。
如果那是不可能的,我正在尝试做的是找到一种方法来发布一个PL / SQL语句,它将更新未知数量的记录(取决于WHERE子句匹配的数量),并返回Ids OracleDataReader中更新的所有记录,使用数据库的单次往返,而不使用存储过程或函数。
我正在使用Oracle 11g使用ODP.NET与现有的C#.NET 4.0代码库进行通信,该代码库使用SQL连接来检索/修改数据。我正在使用的简化测试表定义如下:
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;
从名为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;
我已经创建了一个小型的C#测试程序,试图找出一个ORA-01036“非法变量名称/数字”的错误,其主体看起来像这样:
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为first或last。到目前为止,我发现的最接近的是下面的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 :(得分:5)
我会尝试回答而不是另一条评论。
正如我在一篇评论中所说,纯/简单的select语句在PL / SQL中不起作用。但我说错了,你需要一个存储函数来返回引用光标。
但首先要做的是:您在PL / SQL块中声明的类型“id_array”是PL / SQL类型。它不能在ref cursor 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端未知/可用。