【问题标题】:Different default error handling in Oracle and PostgreSQLOracle 和 PostgreSQL 中不同的默认错误处理
【发布时间】:2020-12-17 14:15:20
【问题描述】:

在 PL/SQL (PL/pgSQL) 代码中遇到错误后,我比较了 Oracle 和 PostgreSQL 的默认行为。为此,我编写了一个类似的 Oracle 和 PostgreSQL 代码,如下所示。

Oracle 代码 (db<>fiddle):

CREATE TABLE table1 (col1 int);

CREATE PROCEDURE raise_error AS
BEGIN
  INSERT INTO table1 VALUES (1/0);
END;
/

INSERT INTO table1 VALUES (1);
CALL raise_error();
COMMIT;
SELECT * FROM table1;

PostgreSQL 代码 (db<>fiddle):

CREATE TABLE table1 (col1 int);

CREATE PROCEDURE raise_error() AS $$
BEGIN
  INSERT INTO table1 VALUES (1/0);
END;
$$ LANGUAGE plpgsql;

BEGIN TRANSACTION; -- disable auto-commit

INSERT INTO table1 VALUES (1);
CALL raise_error();
COMMIT;
SELECT * FROM table1;

注意:在 PostgreSQL 中,我另外运行 BEGIN TRANSACTION 语句来禁用自动提交,因为 Oracle 没有自动提交,我希望这两个代码是相似的。

SELECT * FROM table1 查询的结果在 Oracle 中是一行,在 PostgreSQL 中没有行。

如您所见,Oracle 和 PostgreSQL 中的类似代码给出了不同的结果。默认错误处理的这种差异的原因是什么?

【问题讨论】:

  • 您是否在 Oracle 中启动了事务?在您的 PostgreSQL 示例中,您的事务失败,因此插入失败。这意味着该表仍然是空的。
  • @FrankHeikens,在 Oracle 中,事务是自动启动的。 PostgreSQL、MySQL 和 SQL Server 具有自动提交功能,但 Oracle 没有:stackoverflow.com/a/1366898/4592248
  • 此 Oracle 行为取决于 dbfiddle 客户端在异常后继续,这不是大多数客户端的默认行为。在 sql*plus 中,您可以使用 WHENEVER SQLERROR CONTINUE ROLLBACK 获得 Postgres 行为。

标签: oracle postgresql plsql transactions plpgsql


【解决方案1】:

Oracle 和 PostgreSQL 在这里的行为确实不同。

Oracle 有一些我称之为“语句级回滚”的东西:如果在事务中运行的语句导致错误,则只有该语句的效果会回滚,然后事务继续。

在 PostgreSQL 中,事务中的任何错误都会中止整个事务,因此您只能回滚该事务,它根本没有任何影响。这更多是本着“全有或全无”的精神,但据我所知,SQL标准对此并没有具体规定,因此可以对两者的行为进行争论。

但是,您可以在 PostgreSQL 中使用符合标准的保存点从事务中的错误中“恢复”:

START TRANSACTION;

INSERT INTO table1 VALUES (1);

/* set a savepoint we can revert to */
SAVEPOINT x;

CALL raise_error();

ROLLBACK TO SAVEPOINT x;

/* now the INSERT can be committed */
COMMIT;

但请注意,每笔交易您 don't use too many savepoints(不超过 64 个),否则性能可能会受到影响。

【讨论】:

【解决方案2】:

在 Oracle 中,您使用两个单独的事务,第一个成功,第二个失败。在 PostgreSQL 中,您明确告诉它只使用一个事务并一起处理语句。

在 Oracle 中,如果您使用 PL/SQL 匿名块将语句分组为单个事务:

BEGIN
  INSERT INTO table1 VALUES (1);
  raise_error();
END;
/

并且,等效地在 PostgreSQL 中:

DO
$$
BEGIN
  INSERT INTO table1 VALUES (1);
  CALL raise_error();
END;
$$ LANGUAGE plpgsql;

然后表中将没有行,因为过程中的异常将回滚整个事务。


或者,在 Oracle 中,您可以这样做:

INSERT INTO table1 VALUES (1);

DECLARE
  divide_by_zero EXCEPTION;
  PRAGMA EXCEPTION_INIT( divide_by_zero, -1476 );
BEGIN
  raise_error();
EXCEPTION
  WHEN DIVIDE_BY_ZERO THEN
    ROLLBACK;
END;
/

这将具有将两个事务回滚到最后一次提交的相同效果。

db小提琴OraclePostgreSQL

【讨论】:

    猜你喜欢
    • 2023-02-11
    • 1970-01-01
    • 1970-01-01
    • 2016-09-02
    • 1970-01-01
    • 2013-06-19
    • 2014-10-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多