C#调用PostgreSQL存储过程报42809错误,无法获取结果求助
PostgreSQL存储过程调用错误排查
环境信息
- C# 应用程序
- EntityFramework 4.7.2
- Npgsql 版本 7.0.1
问题解析
执行代码时触发错误:42809: red.common$getreinsurers(timestamp without time zone, timestamp without time zone) is a procedure POSITION: 15,核心原因及细节说明:
- 调用语法错误:PostgreSQL中
PROCEDURE(存储过程)不能用SELECT * FROM ...的方式调用,必须使用CALL语句。错误中的POSITION:15指SQL语句第15个字符的位置,对应SELECT * FROM之后尝试调用存储过程的部分,此处语法违反了PostgreSQL对存储过程的调用规则。 - 参数不匹配:C#代码中添加了
p_sqlcode和p_sqlerrm两个输出参数,但PostgreSQL存储过程定义里未声明这两个参数,导致参数列表不匹配。 - 存储过程语法错误:提供的PostgreSQL存储过程代码中,参数列表重复定义了
p_fromdate和p_todate,且缺少p_sqlcode、p_sqlerrm的参数声明,本身语法不合法。
修复方案
1. 修正PostgreSQL存储过程定义
先修复存储过程的语法错误,补充缺失的输出参数:
CREATE OR REPLACE PROCEDURE red.common$getreinsurers( IN p_fromdate timestamp without time zone, IN p_todate timestamp without time zone, OUT p_refcursorreinsurers refcursor, OUT p_sqlcode double precision, OUT p_sqlerrm text ) AS $BODY$ DECLARE p_refCursorReinsurers$ATTRIBUTES aws_oracle_data.TCursorAttributes; BEGIN p_refCursorReinsurers := NULL; OPEN p_refCursorReinsurers FOR SELECT DISTINCT r.*, tg.name AS tg_name, tg.id AS treaty_group_id, tg.start_date AS tg_start_date, tg.minimum_incident_date AS tg_minimum_incident_date, tg.maximum_incident_date AS tg_maximum_incident_date, tgr.percentage_repayment, tgr.effective_from_date, tgr.effective_to_date FROM red.treaty_groups AS tg JOIN red.treaty_group_reinsurers AS tgr ON tg.id = tgr.treaty_group_id JOIN red.reinsurers AS r ON tgr.reinsurers_id = r.id WHERE tgr.effective_from_date <= p_fromdate AND tgr.effective_to_date >= p_todate; p_refCursorReinsurers$ATTRIBUTES := ROW (TRUE, 0, NULL, NULL); p_sqlcode := 0; p_sqlerrm := ''; END; $BODY$ LANGUAGE plpgsql;
(注:将隐式关联改为显式JOIN语法更清晰;修正参数列表,补充p_sqlcode和p_sqlerrm的输出参数声明)
2. 修正C#调用代码
调整调用方式为CALL语句,修正CommandType及参数逻辑:
DataSet ds = new DataSet(); // 使用CALL调用存储过程,替代SELECT语法 cmd.CommandText = "CALL red.common$getreinsurers(@p_fromdate, @p_todate, @p_refcursorreinsurers, @p_sqlcode, @p_sqlerrm)"; cmd.CommandType = CommandType.Text; // 执行CALL语句需设为Text类型 DateTime dateTime = DateTime.Now; // 输入参数 cmd.Parameters.AddWithValue("@p_fromdate", NpgsqlDbType.Timestamp, dateTime); cmd.Parameters.AddWithValue("@p_todate", NpgsqlDbType.Timestamp, dateTime); // 输出参数:游标 var cursorParam = cmd.Parameters.Add("@p_refcursorreinsurers", NpgsqlDbType.Refcursor); cursorParam.Direction = ParameterDirection.Output; // 输出参数:SQL代码和错误信息 var sqlCodeParam = cmd.Parameters.Add("@p_sqlcode", NpgsqlDbType.Double); sqlCodeParam.Direction = ParameterDirection.Output; var sqlErrParam = cmd.Parameters.Add("@p_sqlerrm", NpgsqlDbType.Text); sqlErrParam.Direction = ParameterDirection.Output; // 执行存储过程并读取游标数据 using (var conn = (NpgsqlConnection)cmd.Connection) { conn.Open(); cmd.ExecuteNonQuery(); // 通过FETCH语句从游标中获取数据 using (var cursorCmd = new NpgsqlCommand($"FETCH ALL IN \"{cursorParam.Value}\"", conn)) { using (var da = new NpgsqlDataAdapter(cursorCmd)) { da.Fill(ds); } } }
(注:PostgreSQL存储过程返回游标后,需通过FETCH语句读取数据;调整CommandType为Text,适配CALL语句执行逻辑)
关键提示
- PostgreSQL中
FUNCTION可用SELECT调用并直接返回结果,而PROCEDURE必须用CALL执行,返回游标时需手动通过FETCH读取数据。 - 必须保证存储过程的参数列表与C#代码中的参数完全匹配,包括参数名、类型和传递方向。
内容的提问来源于stack exchange,提问作者user23750585
相关产品推荐
相关产品推荐

