【问题标题】:Postgresql ignoring if conditionPostgresql忽略条件
【发布时间】:2021-11-18 22:38:26
【问题描述】:

我正在尝试理解 plpgsql 中的事务,我想要一些解释。

我有这个代码:

CREATE OR REPLACE PROCEDURE MaJ(mode IN INT)
AS
$$
DECLARE
r RECORD;
DECLARE
r RECORD;
BEGIN
    FOR r IN SELECT id, fname, lname, bday FROM usr
        LOOP
            IF r.ID % 2 = 0 THEN
                UPDATE usr SET lname = 'KONAN';
                RAISE NOTICE E'fname : %\n', r.lname;
            END IF;
        END LOOP;
    IF mode = 0 THEN
        COMMIT;
    ELSE IF mode = 1 THEN
        ROLLBACK;
    END IF;
END;
  • 首先,在获得并尝试所有可能的解决方案之后,根据过程参数实现提交/回滚的最佳方法是什么。
  • 其次,除了提交回滚问题,postgres 忽略了 if IF r.ID % 2 = 0 THEN 并更新所有条目,谢谢您的解释。

我正在使用 CALL MaJ(VAL); 从另一个过程调用此过程

更新: 也许这段代码更好地描述了 hte 问题: 这是我正在尝试做的事情:

CREATE OR REPLACE PROCEDURE CRDM(Crtrn INOUT INT)
AS
$CRDM$
DECLARE
R RECORD;
BEGIN
    
        FOR R IN SELECT * FROM usr
            LOOP
                IF R.ID % 2 = 0 THEN
                    UPDATE USR SET lname = 'MAGNI' WHERE USR.ID = R.ID;
                END IF;
            END LOOP;
        IF Crtrn = 0 THEN
            COMMIT;
        ELSE
            ROLLBACK;
        END IF;
END;
$CRDM$ LANGUAGE plpgsql;

CREATE OR REPLACE PROCEDURE AMI() 
AS
$AMI$
DECLARE
rtrn INT:=0;
BEGIN
    BEGIN
        CALL CRDM(rtrn);
    END;
END;
$AMI$ LANGUAGE plpgsql;

DO
$$
BEGIN
    CALL AMI();
END;
$$ LANGUAGE plpgsql;

欢迎任何关于我所缺少的内容以及如何更好地思考交易的帮助。

【问题讨论】:

  • 最好是见仁见智。但是为什么不对布尔值使用布尔值呢?
  • @jjanes 很抱歉,这种语言让我很头疼,能否详细说明您的观点,谢谢

标签: postgresql stored-procedures plpgsql psql


【解决方案1】:

只能在新版本中使用ROLLBACKCOMMIT。如果支持这些命令,请检查您使用的版本。

很长时间以来,Postgres 存储过程都没有这些命令并且没有问题。在 PL/pgSQL 中使用 COMMITROLLBACK 并不算太原生——主要是为了更容易地从 Oracle 迁移而实现的,但由于 Oracle 和 Postgres 的事务模型非常不同,因此存在很多限制。

Oracle 在登录后启动隐式事务并等待显式commitrollback。在这些命令之后,Oracle 立即开始新的事务。

在 Postgres 中,每条语句都在事务下执行,就像在 Oracle 中一样。但如果交易是由用户发起的,则用户负责commitrollback。如果用户没有启动事务,则 Postgres 启动事务,当操作引发异常时 Postgres 隐式运行rollback,否则运行commit。例外后只允许rollback

通常在 Postgres 中没有必要使用commitrollback。只需提出一个异常,上层就会完成所有必要的工作。

你的代码不好,请尝试阅读文档和一些关于编程风格的注释。不要使用骆驼符号 - 不区分大小写的语言。

CREATE TABLE foo_tab(id int);
CREATE TABLE boo_tab(id int, b text);

INSERT INTO foo_tab VALUES(1);
INSERT INTO foo_tab VALUES(2);

CREATE OR REPLACE PROCEDURE foo(a bool, b text)
AS $$
DECLARE r record;
BEGIN
  FOR r IN SELECT * FROM foo_tab
  LOOP
    INSERT INTO boo_tab VALUES(r.id, b);
  END LOOP;
  IF a THEN
    COMMIT;
  ELSE
    ROLLBACK;
  END IF;
END
$$ LANGUAGE plpgsql;

CREATE OR REPLACE PROCEDURE foo2(a bool, b text)
AS $$
BEGIN
  CALL foo(a, b);
END;
$$ LANGUAGE plpgsql;

DO $$
BEGIN
  CALL foo2(true, 'Ahoj');
  CALL foo2(false, 'Nazdar');
END;
$$; -- LANGUAGE plpgsql is implicit here

postgres=# SELECT * FROM boo_tab;
┌────┬──────┐
│ id │  b   │
╞════╪══════╡
│  1 │ Ahoj │
│  2 │ Ahoj │
└────┴──────┘
(2 rows)

在 Postgres 13 上测试

【讨论】:

  • 对不起,但它没有用,它抛出了他的错误ERROR: invalid transaction termination CONTEXT: PL/pgSQL function foo(boolean,text) line 9 at COMMIT SQL statement "CALL foo(a, b)" PL/pgSQL function foo2(boolean,text) line 3 at CALL SQL statement "CALL foo2(true, 'Ahoj')" PL/pgSQL function inline_code_block line 3 at CALL SQL state: 2D000 我试图了解这与自动提交的开启和关闭有什么关系,我尝试了这两种情况,但仍然得到同样的错误
  • 关于骆驼案,我很抱歉这是 Oracle 名称,我暂时没有更改,因为我遇到了这个问题的原型设计此功能
  • @Majd - 也许您(或您的客户)已经开始交易。只有在事务或子事务之外调用 CALL 语句时,才能使用 COMMIT、ROLLBACK。 Myabe,您在 psql 中禁用了自动提交 - 这意味着 psql 在后台启动事务,在这种情况下,您不能使用子事务。如果使用 psql,则需要在 psql 中开启 autocommit。
  • 它现在可以工作了,我一次执行所有操作,在执行每个过程之后,然后再执行它工作的匿名块,只有Autocommit on这是正常的,任何建议如果我被要求关闭自动提交,该如何处理?
  • @Majd - 禁用自动提交只是 psql 功能 - 它不是服务器端,所以这对于通常的应用程序来说不是问题。它不可能与禁用的自动提交一起工作 - 只是不支持这种情况。 COMMITROLLBACK 只能在隐式启动事务时使用 - 这是实现限制,无论您喜欢与否都必须接受。
【解决方案2】:

你的“承诺”;和“回滚;”阅读此内容后,我的逻辑看起来不错:https://www.postgresql.org/docs/current/plpgsql-transactions.html

您可能需要“开始;”在您“调用”您的程序之前的交易:我不确定。

您的第二个问题很简单:您的 UPDATE 语句没有 WHERE 子句。确保通过匹配 ID 值仅更新当前循环记录。

【讨论】:

  • 第二个问题现在已经解决了,谢谢你,但是第一个问题仍然存在,令人沮丧。谢谢
  • @majd 第一个问题是什么?您没有描述问题,只是征求意见。
  • @jjanes 提交回滚不起作用,BEGIN; 也不起作用
猜你喜欢
  • 2020-06-05
  • 1970-01-01
  • 2011-01-27
  • 1970-01-01
  • 2022-03-22
  • 2018-07-13
  • 2013-09-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多