必须用CREATE TABLE ... LIKE + INSERT INTO ... SELECT两步才能完整保留主键、索引、AUTO_INCREMENT、字符集等全部结构;CREATE TABLE ... SELECT仅保留字段名、类型和NULL约束,丢失所有元数据,生成“裸表”。

直接说结论:想保留主键、索引、AUTO_INCREMENT、字符集等全部结构,必须用 CREATE TABLE ... LIKE + INSERT INTO ... SELECT 两步;如果只是临时导出一份带数据的快照,CREATE TABLE ... SELECT 一条语句够用,但新表几乎等于“裸表”。
为什么 CREATE TABLE new_t SELECT * FROM old_t 不算真正复制
它本质是“按查询结果反推建表”,只保留字段名、类型、是否允许 NULL 和默认值表达式(部分版本),其余全丢:
• PRIMARY KEY、UNIQUE KEY、FOREIGN KEY 全部消失
• 所有索引(包括 FULLTEXT)不复制
• AUTO_INCREMENT 属性丢失 → 新表插入时可能报错 Field 'id' doesn't have a default value
• 字符集可能降级(比如原表是 utf8mb4_unicode_ci,新表变成 latin1_swedish_ci)
• 如果原表有生成列或 CHECK 约束,该语句直接报错中断
CREATE TABLE ... LIKE 能复制什么、不能复制什么
这是目前 MySQL 单实例内结构保真度最高的原生方式:
✅ 复制:NOT NULL、DEFAULT、AUTO_INCREMENT 当前值、所有索引(主键/唯一/普通/全文)、CHARSET、COLLATE、表注释
❌ 不复制:FOREIGN KEY 约束、触发器、分区定义、存储引擎参数(如 ROW_FORMAT 在某些版本会丢失)
⚠️ 注意:LIKE 不校验目标库是否存在同名表,若 new_t 已存在,直接报错 ERROR 1050 (42S01)
组合操作 LIKE + INSERT SELECT 的实操要点
这是生产环境最稳妥的路径,但细节卡不准照样翻车:
• 大表(比如超 500 万行)务必加 LIMIT 分批插入,避免长事务锁表:INSERT INTO new_t SELECT * FROM old_t LIMIT 0, 100000
• 若原表有外键引用其他表,必须先在目标库建好被引用表,否则插入时报错 ERROR 1452
• 字段顺序或类型不一致时,显式列出字段名:INSERT INTO new_t (id, name, email) SELECT id, name, email FROM old_t
• 跨库操作时,db1.t1 LIKE db2.t2 是非法语法;正确写法是 CREATE TABLE db2.new_t LIKE db1.old_t(需有跨库权限)
• 执行前建议临时调高 sort_buffer_size(比如设为 256MB),加速索引重建
什么时候该放弃 SQL,改用 mysqldump
当遇到以下任一情况,手写 SQL 就不是最优解:
• 表数据量超千万行,INSERT SELECT 太慢或触发内存告警
• 需要跨 MySQL 实例迁移(LIKE 不支持跨实例)
• 要求强一致性(比如主从同步场景),必须用 --single-transaction
• 原表含分区、触发器、外键约束等 LIKE 不支持的元数据
实操命令示例:mysqldump -u user -p --no-create-info --quick db_name old_t | mysql -u user -p db_name new_t(仅导数据)mysqldump -u user -p --single-transaction db_name old_t > backup.sql(结构+数据,可编辑表名后导入)
真正容易被忽略的是字符集继承问题:即使用了 LIKE,如果当前会话的 character_set_database 和原表不一致,新表的 CHARSET 仍可能被覆盖。执行前务必确认:SELECT DEFAULT_CHARACTER_SET_NAME FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = 'your_db';


















