集合操作本身不自动开启事务,仅在当前会话事务上下文中执行,其一致性由事务隔离级别、存储引擎特性共同保障。

集合操作本身不自动开启事务
MySQL 中的 UNION、INTERSECT(8.0.31+)、EXCEPT(8.0.31+)这些集合操作只是查询语法,不涉及数据修改,因此不会触发事务机制。它们运行在当前会话的事务上下文中——如果外面套着 BEGIN / START TRANSACTION,那整个查询就属于那个事务;但集合操作自身既不启动事务,也不提交或回滚。
常见误解是以为 UNION ALL 比 UNION “更快所以更轻量”,其实性能差异主要来自去重逻辑(UNION 隐含 DISTINCT),和事务完全无关。
事务对集合操作结果的一致性起间接作用
当你在事务中执行多条语句,并在其中用集合操作组合中间结果时,事务的隔离级别决定了你能看到哪些数据版本。例如:
- 在
REPEATABLE READ下,事务内多次执行同一SELECT ... UNION ...会看到相同快照,即使其他事务已提交新数据 - 若集合操作里包含子查询(如
(SELECT * FROM t1) UNION (SELECT * FROM t2 WHERE id IN (SELECT id FROM t3))),而t3被并发修改,那最终结果是否“一致”,取决于事务启动时刻的 MVCC 快照
也就是说:集合操作不管理一致性,事务通过快照和锁来保障它。
带写入的集合场景必须显式控制事务
真正容易出错的是把集合操作和 DML 混用,比如用 INSERT ... SELECT ... UNION ... 批量写入。这时事务边界非常关键:
- 没加
START TRANSACTION:每条INSERT独立提交,中途失败会导致部分写入,状态不一致 - 加了事务但没设
autocommit=0:某些客户端驱动默认自动提交,可能让BEGIN失效 - 集合子查询里有
FOR UPDATE:必须确保整条语句在同一个事务中,否则锁会在语句结束时释放,无法保护后续操作
START TRANSACTION; INSERT INTO summary_table SELECT id, SUM(val) FROM sales GROUP BY id UNION ALL SELECT id, 0 FROM missing_ids; -- 若上面某行违反唯一键,整批回滚 COMMIT;
一致性不是集合操作的责任,而是事务 + 隔离级别 + 引擎特性的共同结果
MyISAM 不支持事务,哪怕你写 BEGIN 也没用;InnoDB 支持,但如果你用 READ UNCOMMITTED,集合操作照样能读到脏数据。真正的“一致性”取决于三个层面:
- 引擎层:InnoDB 的行级锁和 MVCC 是基础
- 事务层:是否包裹、是否提交、隔离级别怎么设
- SQL 层:集合操作只是把多个结果集拼起来,不改变底层行的可见性规则
最容易被忽略的是:UNION 结果集的列名和类型由第一个 SELECT 决定,如果后续子查询字段类型隐式转换失败(比如 VARCHAR 和 TEXT 在某些排序规则下冲突),整个语句会报错——这种错误发生在事务执行期,但和一致性无关,纯属语法/类型校验问题。










