You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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,核心原因及细节说明:

  1. 调用语法错误:PostgreSQL中PROCEDURE(存储过程)不能用SELECT * FROM ...的方式调用,必须使用CALL语句。错误中的POSITION:15指SQL语句第15个字符的位置,对应SELECT * FROM之后尝试调用存储过程的部分,此处语法违反了PostgreSQL对存储过程的调用规则。
  2. 参数不匹配:C#代码中添加了p_sqlcode和p_sqlerrm两个输出参数,但PostgreSQL存储过程定义里未声明这两个参数,导致参数列表不匹配。
  3. 存储过程语法错误:提供的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 04:32:04