SAVEPOINT 在循环中的独立性
官方对SAVEPOINT的说明:
Use the SAVEPOINT statement to identify a point in a transaction to which you can later roll back.
简单的理解就是可以部分提交事务
官网的一个例子:
UPDATE employees
SET salary = 7000
WHERE last_name = 'Banda';
SAVEPOINT banda_sal;
UPDATE employees
SET salary = 12000
WHERE last_name = 'Greene';
SAVEPOINT greene_sal;
SELECT SUM(salary) FROM employees;
ROLLBACK TO SAVEPOINT banda_sal;
UPDATE employees
SET salary = 11000
WHERE last_name = 'Greene';
COMMIT;
由于会滚到了banda_sal,所以到检查点banda_sal之间的对表的修改会回退。
如果在另一个循环里使用SAVEPOINT,每次循环会是独立的吗?
1. 创建两张表
create table T_A
(
ID NUMBER
);
create table T_B
(
NAME VARCHAR2(10)
);
2. 创建函数
--模拟当ID=3 插入失败
--在T_A中插入 1,2,3,4,5
CREATE OR REPLACE FUNCTION FUN_1(ID IN NUMBER) RETURN NUMBER IS
BEGIN
IF ID = 3 THEN
RETURN 1;
ELSE
dbms_output.put_line('Fun_1 参数id=' || id);
INSERT INTO T_A VALUES (ID);
RETURN 0;
END IF;
END FUN_1;
--模拟当ID=4时插入失败
--在T_B中插入 'a', 'b', 'c' 'd', 'e'
CREATE OR REPLACE FUNCTION FUN_2(ID IN NUMBER) RETURN NUMBER IS
V_VALUE VARCHAR2(10);
BEGIN
SELECT DECODE(ID, 1, 'a', 2, 'b', 3, 'c', 4, 'd', 5, 'e', 'z')
INTO V_VALUE
FROM DUAL;
IF ID = 4 THEN
RETURN 1;
ELSE
dbms_output.put_line('Fun_2 参数id=' || id);
INSERT INTO T_B VALUES (V_VALUE);
RETURN 0;
END IF;
END FUN_2;
3. 测试
--在开始调用FUN_1和FUN_2之前先建立保存点
--如果FUN_1或者FUN_2发生异常,本次循环就需要回退
--Oracle 10g不支持continue 使用goto模拟了continue
DECLARE
V_CODE1 INTEGER;
V_CODE2 INTEGER;
BEGIN
FOR I IN 1 .. 5 LOOP
SAVEPOINT BANDA_SAL;
V_CODE1 := FUN_1(I);
IF V_CODE1 != 0 THEN
GOTO ERROR_LOOP;
END IF;
V_CODE2 := FUN_2(I);
IF V_CODE2 != 0 THEN
GOTO ERROR_LOOP;
END IF;
<<ERROR_LOOP>>
IF V_CODE1 != 0 OR V_CODE2 != 0 THEN
ROLLBACK TO SAVEPOINT BANDA_SAL;
END IF;
NULL;
END LOOP;
COMMIT;
END;
4. 运行结果
由于当ID=3 和ID=4的时候都发生的回滚,每张表应该
只有3条数据:
SELECT * FROM t_a;
1
2
5
SELECT * FROM t_b;
a
b
e
5.结论
SAVEPOINT在循环中也是独立的。