SQLite不支持直接ALTER TABLE升级结构,必须通过“建新表→迁数据→删旧表→改名”重建实现,因其仅支持ADD COLUMN和RENAME TO,不支持改类型、重命名列或调整列序。

不能直接用一条 ALTER TABLE 把旧表结构“升级”成新表结构——SQLite 不支持原子化字段重命名、类型变更或列顺序调整,MySQL/SQL Server 也受限于锁和事务隔离级别。真正可行的是“重建+迁移”,不是“修改”。
为什么 ALTER TABLE 在多数场景下不适用于结构迁移
常见错误现象:ERROR: cannot rename column "key" to "classification_mode"(PostgreSQL)、Invalid default value for 'status'(MySQL 5.7 严格模式)、SQLite 报 no such column 却明明刚加了——这些都不是语法写错了,而是底层引擎根本不允许某些在线变更。
根本原因在于:DDL 操作在多数数据库中会隐式加表级锁,且字段类型变更(如 VARCHAR(15) → VARCHAR(50))可能触发全表重建;而跨库迁移时,源目标字段语义不一致(比如 statrValue 拼错 → start_value),靠 ALTER COLUMN 无法修复语义映射。
- SQLite 完全不支持
RENAME COLUMN(直到 3.25.0+ 才有实验性支持,但 UniApp 内置 SQLite 版本通常为 3.19 或更早) - MySQL 的
CHANGE COLUMN虽能改名+改类型,但若字段含索引或外键,需先DROP再ADD,极易遗漏 - SQL Server 的
sp_rename只改元数据,不更新视图/存储过程里的引用,上线后直接报错
正确做法:用 CREATE + INSERT INTO SELECT 重建表
这是兼容性最强、可控性最高的方式,尤其适合 UniApp 等嵌入式 SQLite 场景,也适用于 MySQL/PostgreSQL 全量迁移。
核心步骤就是三步:建新表 → 迁数据 → 删旧表 → 改名。关键在中间那步的 INSERT INTO ... SELECT 必须显式处理字段映射、默认值、空值转换。
- 字段名变化(如
statrValue→start_value)必须在SELECT子句里用别名:SELECT ..., `statrValue` AS start_value, ... - 新增字段要给默认值:
SELECT ..., 'active' AS status, ...(不能留 NULL,除非目标字段允许) - 行转列场景(如参数表
t_param→t_new_param)必须用CASE WHEN+MAX()聚合,否则一行变多行 - SQLite 中没有
IF NOT EXISTS对表名的原子判断,建新表前建议先查sqlite_master表
示例(SQLite,适配 UniApp):
CREATE TABLE IF NOT EXISTS t_new_param ( id INTEGER PRIMARY KEY, classification_mode TEXT, size TEXT, start_value TEXT, end_value TEXT, param_id TEXT, status INTEGER ); INSERT INTO t_new_param (id, classification_mode, size, start_value, end_value, param_id, status) SELECT MAX(CASE WHEN `key` = 'ClassificationMode' THEN id END), MAX(CASE WHEN `key` = 'ClassificationMode' THEN `value` END), MAX(CASE WHEN `key` = 'size' THEN `value` END), MAX(CASE WHEN `key` = 'statrValue' THEN `value` END), MAX(CASE WHEN `key` = 'endValue' THEN `value` END), MAX(CASE WHEN `key` = 'paramId' THEN `value` END), 1 FROM t_param GROUP BY (SELECT 1);
容易被忽略的四个落地细节
很多迁移脚本本地跑通,一上真机就丢数据或卡死,问题往往出在这些地方:
- SQLite 在 UniApp 中是单连接、无事务自动提交机制,
BEGIN TRANSACTION必须显式写,且COMMIT不能漏,否则迁移中途崩溃会导致新表有数据、旧表已删、无回滚 - 字段类型不一致时(如源
value VARCHAR(20)存数字字符串'15',目标对应字段是start_value INTEGER),SQLite 会静默转成 0,MySQL 会报 Warning 但继续插,必须提前CAST(value AS INTEGER)或加WHERE value REGEXP '^[0-9]+$' - 目标表主键或唯一约束冲突时,
INSERT INTO ... SELECT直接失败,得先DELETE FROM t_new_param或用INSERT OR REPLACE INTO(SQLite)/INSERT ... ON DUPLICATE KEY UPDATE(MySQL) - 迁移完成后,别忘了删旧表:
DROP TABLE t_param,再ALTER TABLE t_new_param RENAME TO t_param—— 顺序不能反,否则应用读不到原表名
最复杂的不是 SQL 本身,而是确保每一步都有可验证的中间状态:建表成功了吗?插入行数对得上吗?旧表删之前是否已确认新表数据完整?这些检查点必须硬编码进迁移逻辑,不能靠人工 eyeball。

















