我正在努力将现有的应用程序从SQL Server转换为Oracle,我遇到了障碍。我正在尝试将匿名块作为动态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()
的各种组合无效。
任何指针都会受到赞赏,但实际完成我正在寻找的代码的例子将是非常壮观的。
答案 0 :(得分:3)
TLDR:你错过了光标绑定变量上的冒号:
OPEN :rcursor FOR SELECT * FROM CSM_RECORDS;-- WHERE OBJECT_UID IN (t_Ids);
完整答案: 您可能知道,在oracle中有pl / sql上下文(您的匿名块)和SQLplus上下文。这是一个带有SQLplus变量的块,带有相关绑定变量的块,以及最后的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中执行块时,ODP.net正在处理在SQLplus级别完成的准备工作。这是从.net执行的相同块(作为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
答案 1 :(得分:1)
如果您还没有答案,这里有一些我已经确认正在使用的示例代码,您可以将其作为起点。
using System;
using System.Data;
using Oracle.ManagedDataAccess.Client;
using Oracle.ManagedDataAccess.Types;
namespace ConsoleApplication1
{
class Class1
{
[STAThread]
static void Main(string[] args)
{
try
{
string conString = "User Id=scott;Password=tiger;Data Source=orcl;Pooling=false;";
OracleConnection con = new OracleConnection();
con.ConnectionString = conString;
con.Open();
string cmdtxt = "BEGIN " +
"OPEN :1 for select ename, deptno from emp where deptno = 10; " +
"OPEN :2 for select ename, deptno from emp where deptno = 20; " +
"OPEN :3 for select ename, deptno from emp where deptno = 30; " +
"END;";
OracleCommand cmd = con.CreateCommand();
cmd.CommandText = cmdtxt;
OracleParameter p1 = cmd.Parameters.Add("refcursor1",
OracleDbType.RefCursor);
p1.Direction = ParameterDirection.Output;
OracleParameter p2 = cmd.Parameters.Add("refcursor2",
OracleDbType.RefCursor);
p2.Direction = ParameterDirection.Output;
OracleParameter p3 = cmd.Parameters.Add("refcursor3",
OracleDbType.RefCursor);
p3.Direction = ParameterDirection.Output;
cmd.ExecuteNonQuery();
OracleDataReader dr1 =
((OracleRefCursor)cmd.Parameters[2].Value).GetDataReader();
OracleDataReader dr2 =
((OracleRefCursor)cmd.Parameters[1].Value).GetDataReader();
while (dr1.Read() && dr2.Read())
{
Console.WriteLine("Employee Name: " + dr1.GetString(0) + ", " +
"Employee Dept:" + dr1.GetDecimal(1));
Console.WriteLine("Employee Name: " + dr2.GetString(0) + ", " +
"Employee Dept:" + dr2.GetDecimal(1));
Console.WriteLine();
}
Console.WriteLine("Press 'Enter' to continue");
Console.ReadLine();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.InnerException);
Console.WriteLine(ex.Data);
}
}
}
}
答案 2 :(得分:0)
试试这个动态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)
RETURNING OBJECT_UID BULK COLLECT INTO t_Ids;
SELECT * FROM CSM_RECORDS;-- WHERE OBJECT_UID IN (t_Ids);
END;
答案 3 :(得分:0)
感谢大家的反馈。事实证明,在调试此问题时,我在某些时候切换了定义我的查询的变量,因此我在查询时所做的更改在运行应用程序时实际上并未应用。结果,我不确定究竟哪个修复得到了答案。但仅仅为了完整性,我最终使用的是有效的。
首先,运行:
create type id_array as table of number;
(摘自this question)
查询:
DECLARE 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 (SELECT * FROM TABLE(cast(t_Ids as id_array)));
END;
C#看起来像:
OracleCommand getNextNodesC = new OracleCommand(QUERY_DEFINED_ABOVE.Replace("\r\n", "\n"), 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.ReturnValue;
getNextNodesC.Parameters.Add(returnCursor);
return getNextNodesC.ExecuteReader();
b_levitt关于光标绑定变量上缺少冒号的建议虽然是现场开启的。我还需要用&#34; \ n&#34;替换查询中的所有&#34; \ r \ n&#34;
我仍然不清楚为什么我需要运行CREATE TYPE...
而不是仅使用DECLARE TYPE...
,因为后者似乎适用于块的BULK COLLECT
部分,但它现在似乎工作正常。
再次感谢您的帮助。