create table new_table as select * from old_table 仅复制列定义和数据,不复制主键、索引、自增、注释、外键等约束;完整复制需分两步:先用 show create table 获取完整 ddl 并修改表名执行,再 insert into ... select 导入数据。

CREATE TABLE AS 复制表结构和数据的正确写法
直接用 CREATE TABLE new_table AS SELECT * FROM old_table 能复制数据,但不会复制主键、索引、自增、注释、外键约束——它只复制「查询结果的列定义 + 数据」,本质是基于 SELECT 的结果集建表。
常见错误现象:DESCRIBE new_table 发现没有 PRIMARY KEY,id 字段丢了 AUTO_INCREMENT,或者插入新记录时主键冲突、重复值报错。
- 只适合一次性导出快照、ETL 中间表、临时分析表等无约束要求的场景
- 如果原表有
ENUM、SET、JSON或生成列(generated column),部分 MySQL 版本可能降级为VARCHAR或报错 - 不复制
DEFAULT值(MySQL 5.7+ 会保留,但 5.6 不会)
想完整复制表结构(含索引、主键、自增),必须分两步走
第一步建空表:用 SHOW CREATE TABLE old_table 拿到完整 DDL,改表名后执行;第二步导入数据:INSERT INTO new_table SELECT * FROM old_table。
关键点在于:不能跳过 SHOW CREATE TABLE 这步。有人试图用 CREATE TABLE new_table LIKE old_table,它确实能复制键、索引、自增、默认值,但不复制数据——还得补 INSERT,所以两步不可少。
立即学习“PHP免费学习笔记(深入)”;
-
LIKE方式在 MySQL 5.6+ 支持复制分区定义,但不复制触发器、外键约束(外键需手动重建) - 如果原表有外键,
SHOW CREATE TABLE输出里会带CONSTRAINT子句,注意检查新表名是否已存在、引用的父表是否同步迁移 - 大表执行
INSERT ... SELECT期间会锁源表(取决于引擎和事务隔离级别),建议在低峰期操作或加/*+ MAX_EXECUTION_TIME(30000) */防长事务
复制时字段类型被悄悄“缩水”的坑
比如原表有个 VARCHAR(255) 字段,但 SELECT * 在某些客户端或驱动下可能返回长度为 0 的元信息,导致 CREATE TABLE AS 创建成 VARCHAR(1)——这不是 bug,是 MySQL 对「结果集列宽推断」的行为。
更隐蔽的是 TIMESTAMP:如果原字段定义为 TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,CREATE TABLE AS 只会建一个普通 TIMESTAMP,默认值和更新行为全丢。
- 用
SHOW COLUMNS FROM old_table或查INFORMATION_SCHEMA.COLUMNS核对真实类型和属性 - 遇到
TEXT/BLOB字段,确保目标表字符集一致,否则可能因排序规则不匹配导致插入失败 - PHP 中用 PDO 执行
CREATE TABLE AS时,注意关闭PDO::ATTR_EMULATE_PREPARES,否则某些版本会把AS SELECT当作预处理语句解析出错
PHP 中安全执行复制操作的要点
别在 PHP 里拼接表名进 SQL——哪怕你确认输入可控。用白名单校验或参数化方式处理库名/表名(MySQL 不支持预处理占位符用于标识符)。
典型错误写法:$sql = "CREATE TABLE {$newTable} AS SELECT * FROM {$oldTable}",一旦变量被污染就直接 RCE 级风险。
- 用正则
/^[a-zA-Z_][a-zA-Z0-9_]*$/严格校验表名,拒绝任何含点号、反引号、空格的输入 - 执行前先查
SELECT COUNT(*) FROM old_table,避免空表导致CREATE TABLE AS建出无字段的表(MySQL 8.0.23+ 已修复,但老版本仍存在) - 如果要复制跨库表,
CREATE TABLE db2.new AS SELECT * FROM db1.old是合法的,但要求当前用户对两个库都有SELECT权限,且目标库存在
实际操作中,最常被忽略的是外键约束依赖顺序和字符集继承——建完新表立刻 SHOW CREATE TABLE 对比,比事后排查数据不一致省十倍力气。











