pragma autonomous_transaction必须紧随is/as之后、在任何变量声明前,否则编译失败;自治事务需显式commit或rollback,否则抛ora-06519;日志应封装为自治过程调用,不可混入主事务。

PRAGMA AUTONOMOUS_TRANSACTION 必须放在声明区最开头
自治事务不是语法糖,位置错就直接编译失败。PRAGMA AUTONOMOUS_TRANSACTION必须紧挨着 IS 或 AS 之后、在任何变量或游标声明之前,否则报 PLS-00103。把它塞进 BEGIN 块里、IF 分支里、甚至包规范(spec)中,Oracle 都不认。
常见错误写法:
CREATE OR REPLACE PROCEDURE p_log(p_msg VARCHAR2) IS v_ts DATE := SYSDATE; -- 变量声明在前 PRAGMA AUTONOMOUS_TRANSACTION; -- ❌ 错:声明晚于变量 BEGIN INSERT INTO t_log VALUES (p_msg, v_ts); COMMIT; END;
正确写法:
CREATE OR REPLACE PROCEDURE p_log(p_msg VARCHAR2) IS PRAGMA AUTONOMOUS_TRANSACTION; -- ✅ 对:第一行声明 v_ts DATE := SYSDATE; BEGIN INSERT INTO t_log VALUES (p_msg, v_ts); COMMIT; END;
自治过程里必须显式 COMMIT 或 ROLLBACK
自治事务一旦启动,就彻底脱离主事务控制,Oracle 不会帮你兜底。退出时没提交或回滚,就会抛 ORA-06519 并自动回滚整个自治块——日志白写,还带出错。
- 正常流程末尾加
COMMIT -
EXCEPTION分支里必须加ROLLBACK,不能只RAISE;否则异常时事务悬空 - 别在自治过程里再调用另一个自治过程却漏掉它的提交逻辑
示例:
CREATE OR REPLACE PROCEDURE p_log_error(p_msg VARCHAR2) IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO t_log(msg, ts) VALUES (p_msg, SYSDATE);
COMMIT; -- ✅ 必须有
EXCEPTION
WHEN OTHERS THEN
ROLLBACK; -- ✅ 出错也要收尾
RAISE;
END;
主事务异常时只调用自治过程,不碰事务语句
很多开发者在主过程的 EXCEPTION 块里写 INSERT INTO t_log; COMMIT;,以为能保住日志——结果这条 INSERT 仍属于主事务,一回滚全丢。真正有效的做法是:把日志逻辑彻底剥离,只留一个自治过程调用。
错误示范(日志随主事务一起回滚):
BEGIN
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
INSERT INTO t_log VALUES ('deducted', SYSDATE); -- ❌ 属主事务
COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK; -- 连日志也丢了
RAISE;
END;
正确做法:
- 主事务中不写日志表,也不做
COMMIT/ROLLBACK - 只在
EXCEPTION块里调用已定义好的自治日志过程,如p_log_error(SQLERRM) - 确保该过程内部已处理好
COMMIT或ROLLBACK
日志过程参数要捕获真实错误上下文
只记 SQLERRM 和 SQLCODE 不够,容易定位不到源头。关键信息至少包括四类,且类型需匹配:
-
BIZ_CODE:业务单号、批次号等,便于关联追踪 -
ERR_LINE:用DBMS_UTILITY.format_error_backtrace,不是$$PLSQL_LINE(后者只返回当前行) -
ERR_CODE:直接用SQLCODE,注意它是负数(如-1),别取绝对值 -
MSG:用SQLERRM,它已含错误码前缀(如ORA-00001),无需再拼接
调用示例:
EXCEPTION
WHEN OTHERS THEN
p_log_error(
biz_code => 'ORDER_20260929_789',
errorline => DBMS_UTILITY.format_error_backtrace,
errorcode => SQLCODE,
msg => SQLERRM
);
RAISE;
END;
自治事务的独立性很“刚”:它有自己的事务 ID、回滚段、锁资源,主事务看不见它未提交的数据,它也看不见主事务未提交的变更。这种隔离是保障日志不丢的根本,但也是最容易因声明位置或提交遗漏而失效的点。











