SQL事务不支持真正嵌套,BEGIN/COMMIT/ROLLBACK是扁平结构,外层ROLLBACK撤销全部操作,内层COMMIT仅交还控制权;原子性需靠单连接、显式事务控制及错误时主动ROLLBACK保障。

SQL事务本身不支持真正的嵌套事务
多数数据库(如 PostgreSQL、MySQL 默认隔离级别)没有「子事务」概念,BEGIN / COMMIT / ROLLBACK 是扁平的:外层 ROLLBACK 会撤销所有未提交操作,但内层 COMMIT 并不生效——它只是把控制权交还给外层,真正提交只发生在最外层 COMMIT。所谓「嵌套 INSERT」,本质是多个 INSERT 语句在同一个事务块里执行。
用 BEGIN ... COMMIT 包裹多个 INSERT 实现原子性
要让一批 INSERT 要么全成功、要么全失败,只需把它们放在同一事务中:
BEGIN;
INSERT INTO users (name, email) VALUES ('Alice', 'a@example.com');
INSERT INTO profiles (user_id, bio) VALUES (LASTVAL(), 'dev');
INSERT INTO settings (user_id, theme) VALUES (LASTVAL(), 'dark');
COMMIT;如果中间某条失败(比如 email 唯一约束冲突),整个事务不会自动回滚——你得显式处理错误并调用 ROLLBACK。不同客户端行为不同:
• PostgreSQL 的 psql 工具默认遇到错误继续执行后续语句,需加 ON_ERROR_STOP=1 或手动检查;
• Python 的 psycopg2 需捕获 IntegrityError 后调用 conn.rollback();
• MySQL 的 autocommit=False 模式下,同样依赖应用层异常捕获后主动 rollback()。
SAVEPOINT 可模拟“局部回滚”,但不能替代事务边界
若想在事务中途尝试某些操作、失败了也不影响前面已做的插入,可用 SAVEPOINT:
BEGIN; INSERT INTO orders (total) VALUES (100); SAVEPOINT sp1; INSERT INTO order_items (order_id, sku) VALUES (LASTVAL(), 'A123'); -- 可能失败 -- 若这行报错,可 ROLLBACK TO sp1,保留 orders 插入 COMMIT;
注意:
• SAVEPOINT 不是独立事务,它依附于外层事务,ROLLBACK TO 只撤销之后的操作,不影响之前已执行的语句;
• 多个 SAVEPOINT 名称不能重复,否则覆盖;
• PostgreSQL 支持 RELEASE SAVEPOINT,MySQL 不支持该语法;
• 即使用了 SAVEPOINT,最终仍需外层 COMMIT 或 ROLLBACK 来决定整体成败。
常见陷阱:自动提交模式、连接复用和隐式提交语句
最容易被忽略的是事务没真正开启:
- MySQL 默认
autocommit=1,每个INSERT都是独立事务,必须先执行SET autocommit = 0或显式BEGIN; - 某些 ORM(如 Django 的
atomic装饰器)会在函数退出时自动COMMIT,但如果函数内混用原生 SQL 且没统一连接对象,可能跨连接导致事务失效; -
CREATE TABLE、DROP、ALTER等 DDL 在多数数据库中会隐式触发COMMIT,打断当前事务; - PostgreSQL 中
TRUNCATE也隐式提交,而DELETE FROM则不会。
事务原子性靠的是「单连接 + 显式控制 + 错误路径兜底」,不是靠语法嵌套。别指望写个 BEGIN 就万事大吉,得盯住连接状态、错误传播和最终提交动作。

















