文档中Oracle Database PL/SQL Language Reference 11g Release 2 (11.2) ,“隐式回滚”部分以此文本开头:
"Before running an INSERT, UPDATE, DELETE, or MERGE statement, the database marks an implicit savepoint (unavailable to you). If the statement fails, the database rolls back to the savepoint. Usually, just the failed SQL statement is rolled back, not the whole transaction."
因此,如果我在 PL/SQL 程序中运行 SQL 语句,并且该语句失败,则该语句将自动回滚。没关系。
但同一部分以以下文字结尾:
"If you exit a stored subprogram with an unhandled exception, PL/SQL does not assign values to OUT parameters, and does not do any rollback."
这似乎与第一个文本相反:如果我的程序以未捕获的异常结束,则不会进行回滚。但第一个文本说如果 SQL 语句失败则自动回滚。
那么如果我的存储程序包含一个SQL语句,该语句失败,异常没有被捕获,并且我的程序结束了,那么该SQL语句是否应该回滚?文件有矛盾吗?
Stack Overflow 中的相关问题:
更新(已解决):感谢 DrabJay 提供的示例,现在更清楚了:
- 有一件事是 SQL 语句的回滚。
- 另一件事是包含 SQL 语句的程序的回滚。
失败的 SQL 语句的回滚总是会完成(与是否进入程序无关)。程序的回滚取决于调用者:
- 如果回滚应用于调用方,则回滚也应用于程序。
- 如果没有对调用者应用回滚,则不会对程序应用回滚。
如果程序是匿名 block (不存在调用者),则相当于从用户语句中调用,失败的用户语句会自动回滚,因此匿名 block 也被回滚。
我认为文档应该更清楚,特别是“并且不做任何回滚”这几个字:
"If you exit a stored subprogram with an unhandled exception, PL/SQL does not assign values to OUT parameters, and does not do any rollback."
最佳答案
这并不矛盾,但必须准确阅读文档,例如
CREATE TABLE t
(col NUMBER(1) NOT NULL)
/
Table created.
CREATE PROCEDURE insert_t1
AS
BEGIN
INSERT INTO t
(col)
SELECT 1 FROM dual
UNION ALL
SELECT 2 FROM dual;
INSERT INTO t
(col)
SELECT 9 FROM dual
UNION ALL
SELECT 10 FROM dual;
END;
/
Procedure created.
SELECT col
FROM t
/
no rows selected.
INSERT INTO t
SELECT 9 FROM dual
UNION ALL
SELECT 10 FROM dual
/
INSERT INTO t
*
ERROR at line 1:
ORA-01438: value larger than specified precision allowed for this column
SELECT col
FROM t
/
no rows selected.
这表明,假设尝试按照指定的顺序插入两条记录,DML 语句将回滚到执行该语句之前建立的隐式保存点,因为数据库中不存在这两条记录。如果我们继续:
SET SERVEROUTPUT ON SIZE 1000000
DECLARE
CURSOR csr
IS
SELECT col
FROM t
ORDER BY col;
BEGIN
BEGIN
insert_t1;
EXCEPTION
WHEN OTHERS THEN
FOR rec IN csr LOOP
dbms_output.put_line('COL: ' || rec.col);
END LOOP;
RAISE;
END;
END;
/
COL: 1
COL: 2
DECLARE
*
ERROR at line 1:
ORA-01438: value larger than specified precision allowed for this column
ORA-06512: at line 15
这表明,如果您因未处理的异常退出存储子程序,Oracle 不会执行任何回滚,因为第一个插入语句插入的记录仍在表中。然而,如上直接执行DML时,整个第二条插入语句已经回滚到执行第二条语句之前建立的隐式保存点。
但是,如果我们随后尝试查询该表。
SELECT col
FROM t
/
no rows selected.
这表明,如果您因未处理的异常退出匿名 block ,Oracle 确实会进行回滚。这将再次指向执行匿名 block 之前建立的隐式保存点。
关于oracle - PL/SQL : Contradiction in Oracle document on Implicit Rollbacks,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25666826/