扩大varchar长度仅修改系统表pg_attribute.atttypmod,不触碰数据行,前提是新长度≥现有最大数据长度;收缩或跨类型修改则需清理数据、重写表或显式using转换,否则报错中断。

不会影响已有数据,但前提是操作合法、数据合规。 扩大 VARCHAR 长度本身只改 pg_attribute.atttypmod,不碰任何一行数据;收缩或类型转换则可能触发重写、截断甚至报错——关键不在“改没改数据”,而在于你改的是什么、怎么改、数据是否满足新约束。
扩大 VARCHAR 长度:元数据修改,零数据风险
PostgreSQL 把 VARCHAR(N) 的长度限制存为系统表里的一个整数(atttypmod = N + 4),改它就像改配置项。只要新长度 ≥ 当前最长数据字节数,就不会读写任何用户数据行。
- 执行
ALTER TABLE t ALTER COLUMN c TYPE VARCHAR(500)后,\d t显示类型更新,但所有老数据原样不动 - 底层存储和
TEXT完全一致,只是加了长度校验逻辑 - 唯一失败场景:存在某条记录的
LENGTH(c) > 500,此时语句直接报错中断,数据毫发无损
收缩 VARCHAR 长度:必须先清理,否则必然失败
收缩不是“删掉几个字节”,而是要求 PostgreSQL 重新验证并可能重写每一行——因为要确保所有值都能塞进新长度里。它不会自动截断,而是直接拒绝非法操作。
- 错误信息一定是:
ERROR: value too long for type character varying(50) - 必须手动处理超长数据:
UPDATE t SET c = LEFT(c, 50) WHERE LENGTH(c) > 50或DELETE掉它们 - 收缩后表会重写(WAL 写入量大),大表操作耗时明显,且期间持有
AccessExclusiveLock
TEXT ↔ VARCHAR 转换:安全但有缓存陷阱
二者物理存储相同,转换本身不重写数据,但会改变查询计划缓存的“结果类型签名”。这是线上偶发 cached plan must not change result type 的根源。
-
ALTER TABLE t ALTER COLUMN c TYPE TEXT或反向操作,毫秒完成,数据零风险 - 但应用层(如 JDBC、MyBatis)若复用旧执行计划,就会因类型签名不匹配而报错
- 解决方法不是改 SQL,而是让连接重建或执行
DISCARD PLANS(仅当前会话) - ORM 框架如 Spring Boot 建议配合
spring.datasource.hikari.connection-init-sql=DISCARD PLANS自动清理
NUMERIC 精度调整:隐式四舍五入,业务逻辑可能崩
改 NUMERIC(p,s) 会触发数值缩放,PostgreSQL 默认四舍五入,且不提示、不警告——这比字段截断更危险,因为数据“看起来”没丢,实际已变。
-
ALTER TABLE t ALTER COLUMN amt TYPE NUMERIC(10,2)会让99.999变成100.00 - 检查精度损失不能靠肉眼:
SELECT * FROM t WHERE ROUND(amt, 2) != amt - 若业务不允许舍入,必须先
UPDATE截断(如FLOOR(amt * 100) / 100)再改类型
真正危险的从来不是“改长度”这个动作,而是忽略数据现状、绕过依赖检查、或低估缓存一致性影响。尤其在视图/函数索引/分区表场景下,一个 USING 子句漏写、一个 lock_timeout 没设,就可能让变更从秒级变成雪崩。











