修改列定义
改类型、改可空性、改默认值属于高频而又容易出错的一类变更。MySQL 用一个语句改整个列定义,后面没写到的部分会被按缺省处理,因此漏写约束会让它悄悄消失;PostgreSQL 把这几种动作拆成独立的子句,改类型时可以给出转换表达式处理脏数据,但要留意设置非空约束前需要全表校验。
基本信息
| 适用平台 | MySQL 8.0 / PostgreSQL 12 及以上 |
|---|---|
| MySQL 写法 | 一个语句给出完整新定义,改名与改类型可合并 |
| PostgreSQL 写法 | 按动作拆成独立子句分别执行 |
| 转换处理 | PostgreSQL 可用表达式在改类型时处理脏数据 |
| 非空约束 | PostgreSQL 设置时通常需要先扫描校验全表 |
典型使用场景
- 字段扩容:把较短的字符列改宽
- 精度调整:把金额列的精度提升以支持更大范围
- 约束补全:给已有列补上非空约束与默认值
- 类型纠偏:把误存成字符的时间字段改成时间类型
- 迁移前的结构收敛:统一两库之间的列定义
两库写法
两库写法
-- MySQL:给出完整新定义,未写的部分按缺省处理
ALTER TABLE users MODIFY COLUMN age INT NOT NULL DEFAULT 0;
ALTER TABLE users CHANGE COLUMN age user_age INT NOT NULL DEFAULT 0; -- 改名加改类型
-- PostgreSQL:动作拆开执行
ALTER TABLE users ALTER COLUMN age SET NOT NULL;
ALTER TABLE users ALTER COLUMN age SET DEFAULT 0;
ALTER TABLE users ALTER COLUMN age TYPE integer USING age::integer; -- 显式转换表达式
-- 收缩长度或降低精度前先检查超限数据
SELECT COUNT(*) FROM users WHERE CHAR_LENGTH(name) > 20;
-- 改类型前先在临时表上演练一遍
-- CREATE TABLE users_tmp (LIKE users INCLUDING ALL);动作对照
| 动作 | MySQL | PostgreSQL |
|---|---|---|
| 改类型 | 覆盖式定义整体替换 | 单独的类型子句 |
| 改名 | 专用语句可同时改类型 | 重命名语句,与类型分开 |
| 设置非空 | 在完整定义里体现 | 单独子句,需校验现有数据 |
| 设置默认值 | 同样在定义中给出 | 单独的默认值子句 |
| 脏数据转换 | 依赖严格模式报错或截断 | 可用转换表达式逐行处理 |
MySQL 的覆盖式定义是这里最容易出事的地方:只想改类型却漏写非空与默认值,约束会在一次看似无害的变更中丢失。改写前把原定义完整打印出来,逐项对照。
收缩长度、降低精度这类变更要先检查数据:严格模式与宽松模式的处理方式不同,前者报错中断,后者截断写入,两种结果都需要事先预演。