如何在C#中调用带参数且返回记录的Oracle函数?报错求助
解决ORA-06550/PLS-00221:正确调用Oracle函数(返回4列记录)
你遇到的ORA-06550和PLS-00221错误,本质是把Oracle函数(Function)当成了存储过程(Stored Procedure)来调用。函数和存储过程的调用规则完全不同:函数必须有返回值,而存储过程不需要;在C#中调用时,参数处理和CommandType的设置也有明显区别。
下面分两种最常见的场景,给你提供可直接复用的代码方案:
场景1:函数返回单条4列记录(自定义OBJECT/RECORD类型)
假设你的Oracle函数定义是这样的(返回一个包含4列的自定义对象):
-- 先定义自定义对象类型 CREATE OR REPLACE TYPE receipt_record AS OBJECT ( receipt_no VARCHAR2(20), amount NUMBER(10,2), create_date DATE, status VARCHAR2(10) ); -- 定义函数 CREATE OR REPLACE FUNCTION get_receipts( p_store_id IN NUMBER, p_start_date IN DATE, p_end_date IN DATE ) RETURN receipt_record AS v_result receipt_record; BEGIN -- 业务逻辑:查询单条记录赋值给v_result SELECT receipt_no, amount, create_date, status INTO v_result FROM receipt_table WHERE store_id = p_store_id AND create_date BETWEEN p_start_date AND p_end_date AND ROWNUM = 1; RETURN v_result; END; /
C#调用代码
using Oracle.ManagedDataAccess.Client; // 推荐使用托管驱动,无需Oracle客户端 using System; class ReceiptHelper { public static void GetSingleReceipt() { string connString = "Data Source=你的Oracle实例;User Id=用户名;Password=密码;"; using (OracleConnection conn = new OracleConnection(connString)) { conn.Open(); // 用PL/SQL块封装函数调用,把返回的记录拆解成单独的输出参数 string plsql = @" DECLARE v_receipt receipt_record; BEGIN v_receipt := get_receipts(:p_store_id, :p_start_date, :p_end_date); :out_receipt_no := v_receipt.receipt_no; :out_amount := v_receipt.amount; :out_create_date := v_receipt.create_date; :out_status := v_receipt.status; END;"; using (OracleCommand cmd = new OracleCommand(plsql, conn)) { cmd.CommandType = CommandType.Text; // 添加输入参数 cmd.Parameters.Add("p_store_id", OracleDbType.Int32).Value = 101; // 替换成你的门店ID cmd.Parameters.Add("p_start_date", OracleDbType.Date).Value = DateTime.Now.AddMonths(-1); cmd.Parameters.Add("p_end_date", OracleDbType.Date).Value = DateTime.Now; // 添加对应4列的输出参数 cmd.Parameters.Add("out_receipt_no", OracleDbType.Varchar2, 20).Direction = ParameterDirection.Output; cmd.Parameters.Add("out_amount", OracleDbType.Decimal).Direction = ParameterDirection.Output; cmd.Parameters.Add("out_create_date", OracleDbType.Date).Direction = ParameterDirection.Output; cmd.Parameters.Add("out_status", OracleDbType.Varchar2, 10).Direction = ParameterDirection.Output; // 执行命令 cmd.ExecuteNonQuery(); // 读取结果 string receiptNo = cmd.Parameters["out_receipt_no"].Value.ToString(); decimal amount = Convert.ToDecimal(cmd.Parameters["out_amount"].Value); DateTime createDate = Convert.ToDateTime(cmd.Parameters["out_create_date"].Value); string status = cmd.Parameters["out_status"].Value.ToString(); Console.WriteLine($"小票号:{receiptNo},金额:{amount:C},日期:{createDate:yyyy-MM-dd},状态:{status}"); } } } }
场景2:函数返回多行4列结果集(REF CURSOR)
如果你的函数是返回多条记录(比如查询符合条件的所有小票),通常会用SYS_REFCURSOR作为返回类型,函数定义如下:
CREATE OR REPLACE FUNCTION get_receipts( p_store_id IN NUMBER, p_start_date IN DATE, p_end_date IN DATE ) RETURN SYS_REFCURSOR AS v_cursor SYS_REFCURSOR; BEGIN OPEN v_cursor FOR SELECT receipt_no, amount, create_date, status FROM receipt_table WHERE store_id = p_store_id AND create_date BETWEEN p_start_date AND p_end_date; RETURN v_cursor; END; /
C#调用代码
using Oracle.ManagedDataAccess.Client; using System; using System.Data; class ReceiptHelper { public static void GetReceiptList() { string connString = "Data Source=你的Oracle实例;User Id=用户名;Password=密码;"; using (OracleConnection conn = new OracleConnection(connString)) { conn.Open(); // 用PL/SQL块调用函数,获取游标输出 string plsql = @" BEGIN :out_cursor := get_receipts(:p_store_id, :p_start_date, :p_end_date); END;"; using (OracleCommand cmd = new OracleCommand(plsql, conn)) { cmd.CommandType = CommandType.Text; // 输入参数 cmd.Parameters.Add("p_store_id", OracleDbType.Int32).Value = 101; cmd.Parameters.Add("p_start_date", OracleDbType.Date).Value = DateTime.Now.AddMonths(-1); cmd.Parameters.Add("p_end_date", OracleDbType.Date).Value = DateTime.Now; // 输出游标参数 cmd.Parameters.Add("out_cursor", OracleDbType.RefCursor).Direction = ParameterDirection.Output; // 执行并读取结果集 using (OracleDataReader reader = cmd.ExecuteReader()) { while (reader.Read()) { string receiptNo = reader["receipt_no"].ToString(); decimal amount = Convert.ToDecimal(reader["amount"]); DateTime createDate = Convert.ToDateTime(reader["create_date"]); string status = reader["status"].ToString(); Console.WriteLine($"小票号:{receiptNo},金额:{amount:C},日期:{createDate:yyyy-MM-dd},状态:{status}"); } } } } } }
关键注意事项
- 不要混淆函数和存储过程:如果硬要设置
CommandType = CommandType.StoredProcedure,必须显式添加名为RETURN_VALUE的输出参数,但用PL/SQL块的方式更灵活,尤其适合复杂返回类型。 - 参数类型要严格对应:比如Oracle的
DATE对应C#的OracleDbType.Date,NUMBER(10,2)对应OracleDbType.Decimal,避免类型转换错误。 - 资源自动释放:一定要用
using语句包裹连接、命令、阅读器,确保资源及时释放,避免内存泄漏。 - 参数名匹配:Oracle默认支持按位置绑定,但推荐按参数名绑定,避免因参数顺序变化导致的错误。
内容的提问来源于stack exchange,提问作者istudent
相关产品推荐
相关产品推荐

