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

PostgreSQL跨银行转账系统报错:bank1_add_amount过程不存在

问题:PostgreSQL银行转账系统报错“bank1_add_amount过程不存在”

问题描述

开发模拟5家银行间转账的PostgreSQL系统时,执行代码出现错误,提示bank1_add_amount(character varying, double precision)过程不存在。移除EXECUTE FORMAT('CALL bank%s_add_amount(%L, %s)', bank, client_name, amount);语句后错误消失,但该语句是实现转账功能必需的。

错误信息(中文翻译)

错误: 错误: 过程bank1_add_amount(character varying, double precision)不存在
第1行: CALL bank1_add_amount('Client 1'::varchar, -50::float)
             ^
提示: 没有匹配指定名称和参数类型的过程。您可能需要添加显式的类型转换。
查询: CALL bank1_add_amount('Client 1'::varchar, -50::float)
上下文: PL/pgSQL函数add_amount(integer,character varying,double precision),第6行EXECUTE处
SQL指令 « CALL add_amount(1, 'Client 1', -50) »
PL/pgSQL函数transfer_amount(integer,integer,character varying,double precision),第3行EXECUTE处
SQL指令 « CALL transfer_amount(ROW.bank_from, ROW.bank_to, ROW.client_name, ROW.amount) »
PL/pgSQL函数perform_end_of_day(),第6行CALL处

SQL状态: 42883

问题原因

你用PREPARE创建的是预准备语句(Prepared Statement),而非存储过程(Procedure)。CALL关键字仅用于调用存储过程,预准备语句需要用EXECUTE执行,这是导致报错的核心原因。此外,预准备语句的作用域仅限当前会话,DO块执行完毕后这些预准备语句可能已失效,且重复创建的方式也不合理。

解决方案

方案1:将预准备语句替换为存储过程

把每个bankX_add_amount从预准备语句改成存储过程,这样就能用CALL正常调用:

-- 替换原来的DO块,创建存储过程
CREATE OR REPLACE PROCEDURE bank1_add_amount(client_name varchar, amount float)
LANGUAGE plpgsql
AS $$
BEGIN
    UPDATE bank1_accounts
    SET balance = balance + amount
    WHERE client = client_name;
END $$;

CREATE OR REPLACE PROCEDURE bank2_add_amount(client_name varchar, amount float)
LANGUAGE plpgsql
AS $$
BEGIN
    UPDATE bank2_accounts
    SET balance = balance + amount
    WHERE client = client_name;
END $$;

-- 同理创建bank3_add_amount、bank4_add_amount、bank5_add_amount存储过程

之后原add_amount过程的CALL语句就能正常执行。

方案2:直接在add_amount中动态执行UPDATE(推荐)

无需为每个银行单独创建存储过程或预准备语句,直接在add_amount过程里动态生成UPDATE语句,简化代码:

-- 删除原来所有创建预准备语句的DO块
-- 重新定义add_amount过程
CREATE OR REPLACE PROCEDURE add_amount(bank integer, client_name varchar, amount float)
LANGUAGE plpgsql
AS $$
BEGIN
    -- 动态检查客户是否在对应银行有账户
    IF NOT EXISTS (
        SELECT 1 FROM (EXECUTE FORMAT('SELECT client FROM bank%s_accounts WHERE client = %L', bank, client_name)) AS t
    ) THEN
        RAISE EXCEPTION '客户 % 在银行 % 无有效账户', client_name, bank;
    END IF;
    -- 动态执行更新余额的语句
    EXECUTE FORMAT(
        'UPDATE bank%s_accounts SET balance = balance + $1 WHERE client = $2',
        bank
    ) USING amount, client_name;
END $$;

这种方式避免了重复代码,也不会混淆预准备语句和存储过程的使用方式。

修正后的完整代码示例

-- 清理现有表
DROP TABLE IF EXISTS bank1_accounts, bank2_accounts, bank3_accounts, bank4_accounts,
bank5_accounts, clients, transfer_logs, end_of_day_transfers;

-- 创建基础表结构
CREATE TABLE IF NOT EXISTS clients
(
    name varchar PRIMARY KEY
);

CREATE TABLE IF NOT EXISTS transfer_logs
(
    id serial PRIMARY KEY,
    bank_from integer,
    bank_to integer,
    client_name varchar,
    amount float,
    success bool
);

CREATE TABLE IF NOT EXISTS bank1_accounts
(
    id int PRIMARY KEY,
    balance float,
    client varchar,
    FOREIGN KEY (client) REFERENCES clients(name),
    CHECK (balance >= -500)
);

CREATE TABLE IF NOT EXISTS bank2_accounts
(
    id int PRIMARY KEY,
    balance float,
    client varchar,
    FOREIGN KEY (client) REFERENCES clients(name),
    CHECK (balance >= -500)
);

CREATE TABLE IF NOT EXISTS bank3_accounts
(
    id int PRIMARY KEY,
    balance float,
    client varchar,
    FOREIGN KEY (client) REFERENCES clients(name),
    CHECK (balance >= -500)
);

CREATE TABLE IF NOT EXISTS bank4_accounts
(
    id int PRIMARY KEY,
    balance float,
    client varchar,
    FOREIGN KEY (client) REFERENCES clients(name),
    CHECK (balance >= -500)
);

CREATE TABLE IF NOT EXISTS bank5_accounts
(
    id int PRIMARY KEY,
    balance float,
    client varchar,
    FOREIGN KEY (client) REFERENCES clients(name),
    CHECK (balance >= -500)
);

CREATE TABLE end_of_day_transfers
(
    id serial,
    bank_from integer,
    bank_to integer,
    client_name varchar,
    amount float
);

-- 插入测试数据
INSERT INTO clients VALUES ('Client 1');
INSERT INTO clients VALUES ('Client 2');
INSERT INTO clients VALUES ('Client 3');
INSERT INTO clients VALUES ('Client 4');

INSERT INTO bank1_accounts VALUES (8, 2000, 'Client 2');
INSERT INTO bank1_accounts VALUES (12, 650, 'Client 1');
INSERT INTO bank2_accounts VALUES (2, 300, 'Client 3');
INSERT INTO bank3_accounts VALUES (2, 300, 'Client 4');

-- 重新定义add_amount过程(动态执行UPDATE)
CREATE OR REPLACE PROCEDURE add_amount(bank integer, client_name varchar, amount float)
LANGUAGE plpgsql
AS $$
BEGIN
    -- 动态检查客户账户是否存在
    IF NOT EXISTS (
        SELECT 1 FROM (EXECUTE FORMAT('SELECT client FROM bank%s_accounts WHERE client = %L', bank, client_name)) AS t
    ) THEN
        RAISE EXCEPTION '客户 % 在银行 % 无有效账户', client_name, bank;
    END IF;
    -- 动态更新余额
    EXECUTE FORMAT(
        'UPDATE bank%s_accounts SET balance = balance + $1 WHERE client = $2',
        bank
    ) USING amount, client_name;
END $$;

-- 转账过程
CREATE OR REPLACE PROCEDURE transfer_amount(bank_from integer, bank_to integer, client_name varchar, amount float)
LANGUAGE plpgsql
AS $$
BEGIN
    CALL add_amount(bank_from, client_name, -amount);
    CALL add_amount(bank_to, client_name, amount);
    INSERT INTO transfer_logs (bank_from, bank_to, client_name, amount, success) VALUES (bank_from, bank_to, client_name, amount, TRUE);
EXCEPTION WHEN OTHERS THEN
    INSERT INTO transfer_logs (bank_from, bank_to, client_name, amount, success) VALUES (bank_from, bank_to, client_name, amount, FALSE);
    RAISE; -- 可选:重新抛出异常,便于调试
END $$;

-- 日终处理过程
CREATE OR REPLACE PROCEDURE perform_end_of_day()
LANGUAGE plpgsql
AS $$
DECLARE
    ROW record;
BEGIN
    FOR ROW IN SELECT * FROM end_of_day_transfers
    LOOP
        CALL transfer_amount(ROW.bank_from, ROW.bank_to, ROW.client_name, ROW.amount);
    END LOOP;
END $$;

-- 测试执行
INSERT INTO end_of_day_transfers (bank_from, bank_to, client_name, amount) VALUES (1, 2, 'Client 1', 50);
CALL perform_end_of_day();
SELECT * FROM transfer_logs;

内容的提问来源于stack exchange,提问作者Lapin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 20:39:54