编程 MySQL 9.7.4 收紧 EXCHANGE PARTITION:两边 CHECK 都要过,交换前先跑一条 SELECT 预检

2026-10-22 21:02:17

MySQL 9.7.4 收紧 EXCHANGE PARTITION:两边 CHECK 都要过,交换前先跑一条 SELECT 预检

MySQL 9.7.4(2026-10-20)的 SQL Syntax Notes 修了 Bug #120206 / #39634208。此前 ALTER TABLE ... EXCHANGE PARTITION 能把违反目标表 CHECK 约束的行搬过去,也能交换生成列表达式不同的两张表;现在行会做双向校验,生成列定义不一致直接拒绝。

报错形如:

ERROR 3819 (HY000): Check constraint 't_swap_chk_1' is violated.

发布说明:

双向校验意味着什么

分区交换不是单向搬运,而是两边对调:分区 p0 里的行进入普通表,普通表里的行进入 p0。因此两边的 CHECK 都要满足——源分区里的行要过目标表的 CHECK,目标表里的行也要过源表的 CHECK。只查一边,交换完成后总有一侧留下不合约束的数据。

ALTER TABLE t_swap EXCHANGE PARTITION p0 WITH TABLE t_stage;

这条语句现在会在移动数据前把两侧的约束都验证一遍。

WITHOUT VALIDATION 跳过的只是分区边界

WITHOUT VALIDATION 跳过的是分区边界检查(partition-bound checks),即行是否落在该分区的 VALUES 范围内。它不跳过 CHECK 约束校验。即使加上这个子句,CHECK 仍然照常执行,违反时依旧返回 3819。

生成列为什么必须一致

交换后行会跟随目标表的定义读取生成列,表达式不同,同一行在两边算出的值就不同;生成列上如果有索引,索引内容也会对不上。类型不一致同理,存储表示和比较语义都可能变化。因此表达式不一致会被直接拒绝,类型也要求一致。

交换前可以先用 information_schema.COLUMNS 对比两张表的生成列:

SELECT TABLE_NAME, COLUMN_NAME, COLUMN_TYPE, GENERATION_EXPRESSION
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'db'
AND TABLE_NAME IN ('t_swap', 't_stage')
AND GENERATION_EXPRESSION <> ''
ORDER BY COLUMN_NAME, TABLE_NAME;

COLUMN_TYPE 与 GENERATION_EXPRESSION 两项逐一比对即可。

上线前的一条 SELECT 预检

不用等 DDL 报错,先按目标表的 CHECK 表达式把待交换数据筛一遍。假设 t_swap 上有 CHECK (amount >= 0) 和 CHECK (status IN ('new','paid')),交换前查 t_stage:

SELECT *
FROM t_stage
WHERE NOT (amount >= 0)
OR NOT (status IN ('new','paid'));

返回空集才说明这批行满足目标表约束。反向也要查一次,确认 p0 里的行满足 t_stage 的 CHECK:

SELECT *
FROM t_swap PARTITION (p0)
WHERE NOT (amount >= 0)
OR NOT (status IN ('new','paid'));

表达式按实际约束替换;多个 CHECK 之间是 AND 关系,违反任一条件就会失败,所以用 OR 把各条件的反写连起来。CHECK 结果为 NULL 时视为通过,WHERE NOT (...) 的选中行为与之一致。

约束表达式本身可以从元数据里取:

SELECT tc.CONSTRAINT_NAME, cc.CHECK_CLAUSE
FROM information_schema.TABLE_CONSTRAINTS tc
JOIN information_schema.CHECK_CONSTRAINTS cc
USING (CONSTRAINT_SCHEMA, CONSTRAINT_NAME)
WHERE tc.TABLE_SCHEMA = 'db'
AND tc.TABLE_NAME = 't_swap'
AND tc.CONSTRAINT_TYPE = 'CHECK';

DDL 失败之后

交换是原子的:校验不通过时语句报错回滚,数据没有被移动,两张表保持交换前的状态。修正不合约束的行,或把生成列定义改成一致,重新执行即可,不需要手工把数据搬回去。

同版本的其他改动

  • InnoDB 行大小估算:此前一个死锁修复让行大小估算更准,同时收紧了 CREATE TABLE / ALTER TABLE 的校验,拒绝了早期 LTS 版本能接受的表定义。9.7.4 起,多数 DDL 恢复旧版校验行为,只在防止 B-tree 死锁需要的地方保留精确估算;ALTER TABLE ... ALGORITHM=INSTANT 继续使用更严格的算法,以安全处理较大的默认值。(Bug #120323 / #39249507)
  • 四段版本号:MAJOR.MINOR.PATCH.UPDATE 形式的精确版本标识可通过 VERSION_WITH_UPDATE()、version_with_update 变量和 mysqld --version-with-update 获取。(WL #17318)
  • FULLTEXT 分词失败:延迟分词时外部 MeCab 解析器失败,基础行可能被提交而缺少完整的 token posting,导致搜索漏掉该行。现在解析器失败会向客户端返回 warning,autocommit 语句同样包含在内。
复制全文 生成海报 MySQL 分区表 DDL 约束校验

推荐文章

程序员茄子在线接单