我正在努力将现有应用程序从 SQL Server 转换到 Oracle,但遇到了障碍。我正在尝试将匿名 block 作为动态 SQL 执行并返回结果集。但是,我尝试过的任何事情似乎都无法返回任何值。由于设计限制,存储过程不可用。
我的查询定义为:
DECLARE type id_array IS TABLE OF number;
t_Ids id_array;
BEGIN
UPDATE CSM_RECORDS SET MIGRATION_STATE = 1, LAST_UPDATE = CURRENT_DATE
WHERE OBJECT_UID IN
(SELECT OBJECT_UID
FROM CSM_RECORDS obj
WHERE MIGRATION_STATE = 0
AND ROWNUM <= :BatchSize)
AND (:BatchName IS NULL OR obj.BATCH_NAME = :BatchName)
RETURNING OBJECT_UID BULK COLLECT INTO t_Ids;
OPEN rcursor FOR SELECT * FROM CSM_RECORDS;-- WHERE OBJECT_UID IN (t_Ids);
END;
你可以看到我已经注释掉了游标上的 WHERE 子句,试图让任何返回。
在 C# 方面,我有:
OracleCommand getNextNodesC = new OracleCommand(SQL_AS_SHOWN_ABOVE, conn);
getNextNodesC.BindByName = true;
OracleParameter batchSizeP = new OracleParameter("BatchSize", OracleDbType.Int32);
batchSizeP.Value = batchSize;
getNextNodesC.Parameters.Add(batchSizeP);
OracleParameter batchNameP = new OracleParameter("BatchName", OracleDbType.Varchar2);
batchNameP.Value = batchName;
getNextNodesC.Parameters.Add(batchNameP);
OracleParameter returnCursor = new OracleParameter("rcursor", OracleDbType.RefCursor);
returnCursor.Direction = ParameterDirection.Output;
getNextNodesC.Parameters.Add(returnCursor);
getNextNodesC.ExecuteNonQuery();
return ((Oracle.ManagedDataAccess.Types.OracleRefCursor)returnCursor.Value).GetDataReader();
最终目标是我可以使用的 DbDataReader
,但在上面的代码中,returnCursor.Value
似乎仍为空。我尝试了 Output
与 ReturnValue
参数以及 ExecuteNonQuery()
和 ExecuteReader()
的各种组合,但没有有用。
任何指针都将不胜感激,但实际上可以完成我正在寻找的代码示例将是非常壮观的。
最佳答案
TLDR:您的光标绑定(bind)变量上缺少冒号:
OPEN :rcursor FOR SELECT * FROM CSM_RECORDS;-- WHERE OBJECT_UID IN (t_Ids);
完整答案: 您可能知道,在 oracle 中有 pl/sql 上下文(您的匿名 block )和 SQLplus 上下文。这是一个带有 SQLplus 变量的 block ,一个带有相关绑定(bind)变量的 block ,最后是一个 SQLplus 打印:
var rcursor refcursor
var fromDate varchar2(50)
var toDate varchar2(50)
exec :fromDate := '1-mar-2014';
exec :toDate := '1-apr-2014';
begin
open :rcursor for
SELECT
trunc(to_date(:fromDate,'dd-mon-yyyy')) + NUMTODSINTERVAL(n,'day') AS Full_Date
FROM (
select (level-1) n
from dual
connect by level-1 <= trunc(to_date(:toDate,'dd-mon-yyyy')) - trunc(to_date(:fromDate,'dd-mon-yyyy'))
)
;
end;
/
print rcursor
在 .net 中执行 block 时,ODP.net 负责在 SQLplus 级别完成的准备工作。这是从 .net 执行的相同 block (作为 nunit 测试):
[Test]
public void RefCursorFromBatch()
{
OracleCommand cmd = new OracleCommand();
cmd.CommandText = @"
begin
open :rcursor for
SELECT
trunc(to_date(:fromDate,'dd-mon-yyyy')) + NUMTODSINTERVAL(n,'day') AS Full_Date
FROM (
select (level-1) n
from dual
connect by level-1 <= trunc(to_date(:toDate,'dd-mon-yyyy')) - trunc(to_date(:fromDate,'dd-mon-yyyy'))
)
;
end;";
cmd.BindByName = true;
cmd.Parameters.Add("fromDate", OracleDbType.Date).Value = DateTime.Today.AddDays(-30);
cmd.Parameters.Add("toDate", OracleDbType.Date).Value = DateTime.Today;
cmd.Parameters.Add("rcursor", OracleDbType.RefCursor).Direction = ParameterDirection.Output;
using (cmd.Connection = new OracleConnection("..."))
{
cmd.Connection.Open();
var reader = cmd.ExecuteReader();
OracleDataAdapter da = new OracleDataAdapter(cmd);
DataTable dt = new DataTable();
da.Fill(dt);
Assert.Greater(dt.Rows.Count, 0);
}
}
您可以在此处阅读更多信息:http://www.brothersincode.com/post/executing-SQL-Plus-Batches-from-Net.aspx
关于c# - 从 C# 中的 PL/SQL 匿名 block 返回游标,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22821112/