oracle ref cursor 是 oracle 服务端的结果集指针,非实际数据;.net 中必须显式声明为 oracledbtype.refcursor、direction=output,再通过 oracledataadapter.fill() 或 executereader() 获取数据,不可直接当 datatable 使用。

Oracle REF CURSOR 是什么,为什么不能直接当 DataTable 用
REF CURSOR 是 Oracle 的游标类型,本质是服务端打开的一个结果集指针,不是数据本身。C# 的 OracleCommand 默认不自动获取它指向的数据,你调用 ExecuteNonQuery() 或漏设参数方向,只会得到 null 或报错 ORA-06550,根本拿不到行。
关键点在于:REF CURSOR 必须声明为 OracleDbType.RefCursor 类型,并且方向必须是 ParameterDirection.Output;否则驱动不知道该把它当数据源还是占位符。
- 别用
ExecuteScalar()—— 它只取第一列第一行,REF CURSOR 不适用 - 别漏写
command.BindByName = true(尤其有多个参数时),否则位置绑定容易错乱 - 存储过程里必须用
OPEN :cursor_name FOR SELECT ...显式打开,不能只声明变量
用 OracleDataAdapter 填充 DataSet 的标准流程
这是最稳妥、兼容性最好的方式,尤其适合需要多结果集或后续要绑定到 WinForms/WPF 控件的场景。核心是让 OracleDataAdapter 自动处理 REF CURSOR 的提取和转换。
var conn = new OracleConnection(connectionString);
var cmd = new OracleCommand("PKG_MY.GET_EMPLOYEES", conn);
cmd.CommandType = CommandType.StoredProcedure;
<p>var cursorParam = cmd.Parameters.Add("p_cursor", OracleDbType.RefCursor);
cursorParam.Direction = ParameterDirection.Output;</p><p>var adapter = new OracleDataAdapter(cmd);
var ds = new DataSet();
adapter.Fill(ds); // 这一步才真正执行并读取游标内容</p><p>// 结果在 ds.Tables[0] 里
</p>
- 存储过程名必须带包名(如
"PKG_MY.GET_EMPLOYEES"),不然 Oracle 找不到 - 如果过程有多个 REF CURSOR 输出参数,
Fill()会按声明顺序填入ds.Tables[0]、ds.Tables[1]… - 确保连接字符串里包含
Unicode=true,否则中文字段可能乱码
用 OracleDataReader 逐行读取更轻量的场景
如果你只需要遍历一次、不做缓存、也不需要 Schema 信息,OracleDataReader 内存占用更低,响应更快。但要注意:游标必须在连接打开状态下读取,且不能跳过行或倒退。
conn.Open();
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
string name = reader.GetString(0);
int id = reader.GetInt32(1);
// 处理每一行
}
}
// reader.Close() 或 using 块结束时,底层游标才关闭
- 别在
reader.Read()外访问字段(比如先reader.FieldCount再循环),某些驱动版本会抛InvalidOperationException - 如果存储过程里游标没
OPEN就返回,ExecuteReader()会直接报ORA-1001: invalid cursor - 不要在
reader活跃时调用conn.Close(),否则读取中途断连
常见 ORA 错误对应排查点
出错时别急着改 C# 代码,先看 Oracle 端是否已就绪。下面三个错误几乎都指向服务端配置或参数匹配问题:
-
ORA-06550: line X, column Y: PLS-00306: wrong number or types of arguments→ 存储过程中参数名/数量/CURSOR 变量名和 C# 里Parameters.Add()不一致 -
ORA-01001: invalid cursor→ 存储过程里忘了OPEN p_cursor FOR ...,或用了RETURN而非OUT参数传游标 -
ORA-06504: PL/SQL: Return types of call statement and actual do not match→ C# 中参数类型写成OracleDbType.Cursor(旧版写法)而非OracleDbType.RefCursor
REF CURSOR 的“复杂”不在 C# 侧,而在它要求前后端严格约定:谁打开、谁关闭、类型是否匹配、连接生命周期是否可控。一旦某环松动,错误就出现在最不直观的地方。











