SELECT INTO和CREATE TABLE...SELECT能一步完成结构与数据复制,但不复制主键、索引、约束等元数据,仅基于查询结果推导列定义,目标表必须不存在,否则报错。

直接用 SELECT INTO 或 CREATE TABLE ... SELECT 能一步完成,但必须清楚它们不复制主键、索引、约束——这不是 bug,是设计如此。
SQL Server 用 SELECT INTO:目标表必须不存在
这是最常卡住人的地方:语句执行前,new_table 在数据库里绝对不能存在,否则报错 Msg 2714, Level 16, State 6: There is already an object named 'xxx' in the database.。
- 只复制结构(空表):
SELECT * INTO new_table FROM old_table WHERE 1 = 0; - 全量复制:
SELECT col1, col2, col3 INTO new_table FROM old_table;(别用*,列顺序和表达式会影响 NULL 性与类型推断) -
IDENTITY列会自动继承,但新表仍是堆表(无聚集索引),哪怕源表有 - 跨库可行:
SELECT * INTO targetdb.dbo.newtable FROM sourcedb.dbo.oldtable;,但不能跨实例
MySQL 用 CREATE TABLE ... LIKE + INSERT INTO ... SELECT:要两步
想保留主键、索引、AUTO_INCREMENT,必须分两步走;CREATE TABLE ... SELECT 看似一步,但会丢掉所有约束。
- 先完整复制结构:
CREATE TABLE new_table LIKE old_table; - 再插入数据:
INSERT INTO new_table SELECT * FROM old_table; - 大数据量时加
LIMIT分批,避免长事务锁表:INSERT INTO new_table SELECT * FROM old_table LIMIT 100000; - 如果只用
CREATE TABLE new_table AS SELECT * FROM old_table;,新表没有主键、索引、自增属性,仅字段定义和默认值被保留
为什么复制完索引/主键不见了?
因为 SELECT INTO 和 CREATE TABLE ... SELECT 的本质是「基于查询结果推导建表」,不是「克隆元数据」。它只看 SELECT 出来的列名、表达式类型、NULL 属性,完全不读取 sys.indexes 或 information_schema.KEY_COLUMN_USAGE。
- 原表的
PRIMARY KEY(id)不会出现在新表里,得手动ALTER TABLE new_table ADD PRIMARY KEY (id); -
SHOW CREATE TABLE old_table输出的 DDL 才包含全部对象,可修改表名后执行 - 函数或计算列会让类型“缩水”:比如
RTRIM(name)可能让目标列变成VARCHAR(1),用sp_help 'new_table'(SQL Server)或DESCRIBE new_table(MySQL)确认实际定义
跨数据库或要完整复刻时,别硬扛
SSMS 右键 → “编写表脚本为” → “CREATE 到” 是最稳的起点;MySQL 用 SHOW CREATE TABLE old_table 拿到原始 DDL,改名后执行,再 INSERT 数据。手写脚本看着麻烦,但能控制每一个 ENGINE、CHARSET、COMMENT 和索引名——这些恰恰是线上迁移最容易出问题的地方。

















