CREATE TABLE … AS SELECT 最快复制表结构和数据,但不复制主键、索引、约束、默认值和注释;需额外处理序列、默认值等元信息,且备份应优先使用 mysqldump/pg_dump 等专业工具。

用 CREATE TABLE … AS SELECT 复制表结构加数据最直接
想快速备份一张表,又不想写一堆 CREATE TABLE 语句定义字段,CREATE TABLE new_table AS SELECT * FROM old_table 是最快路径。它自动复制字段名、类型(部分数据库会降级精度,比如 PostgreSQL 保留类型,MySQL 可能转成非空默认值)、甚至表达式结果——但不复制主键、索引、约束、默认值、注释。
常见错误现象:ERROR: column "id" violates not-null constraint —— 因为源表 id 是 SERIAL 或带 DEFAULT nextval(……),而 AS SELECT 只取值,不继承默认逻辑;恢复时插入新行就会失败。
- 如果只要结构不要数据,加
WHERE FALSE或WHERE 1=0:CREATE TABLE backup_table AS SELECT * FROM original_table WHERE FALSE - PostgreSQL 中想连序列一起复制,得额外
CREATE SEQUENCE并ALTER TABLE …… ALTER COLUMN id SET DEFAULT nextval(……) - MySQL 8.0+ 支持
CREATE TABLE …… LIKE复制结构(含键和约束),再用INSERT INTO …… SELECT补数据,更稳妥
JOIN 在跨表备份中不是用来“同步”的,而是用来“校验”和“补漏”
有人以为 INSERT INTO t1 SELECT …… FROM t2 JOIN t3 能自动做增量同步,其实不是。JOIN 在这里只是查询时关联条件,不解决冲突、不判断是否存在、不处理更新逻辑。真要靠 JOIN 做数据同步,必须配合 ON CONFLICT(PostgreSQL)或 INSERT …… ON DUPLICATE KEY UPDATE(MySQL),否则重复主键直接报错。
使用场景:从日志表 log_events 关联用户表 users 提取完整信息,生成一份带姓名的快照表 report_snapshot,后续不再更新——这是静态备份,不是持续同步。
- 别在
SELECT里用LEFT JOIN后无条件INSERT,NULL 值可能污染目标表字段(比如NOT NULL字段被插进 NULL) - MySQL 中
INSERT IGNORE会静默跳过冲突,但不会告诉你哪几条被跳了;用SHOW WARNINGS才能看到 - PostgreSQL 的
INSERT …… ON CONFLICT DO NOTHING不返回影响行数,调试时建议先用RETURNING *看实际插入了什么
用 INSERT … SELECT + WHERE NOT EXISTS 避免重复插入
当目标表已有部分数据,只想追加源表里还没有的记录时,WHERE NOT EXISTS 比 LEFT JOIN …… WHERE t2.id IS NULL 更清晰、通常也更快——尤其目标表有主键或唯一索引时,数据库能走反向索引查找。
性能影响:如果子查询里没限制字段(比如写 SELECT *),或没给关联字段建索引,NOT EXISTS 可能全表扫描源表多次;线上大表慎用。
- 正确写法:
INSERT INTO users_backup SELECT * FROM users u WHERE NOT EXISTS (SELECT 1 FROM users_backup ub WHERE ub.id = u.id) - MySQL 5.7+ 对
NOT EXISTS优化较好;但低于 5.7 或 MariaDB 10.2 之前,有时会比LEFT JOIN慢,建议实测EXPLAIN - PostgreSQL 中若
users_backup.id无索引,NOT EXISTS会变全表扫描,务必确认目标表关键字段有索引
mysqldump/pg_dump 是结构 + 数据备份的事实标准,别手写 JOIN 替代
想把整库或单表导出成 SQL 文件用于恢复,mysqldump 和 pg_dump 不仅处理表结构、数据、索引、约束,还自动处理字符集、权限、函数依赖、序列当前值等细节。自己用 INSERT …… SELECT 或 JOIN 拼出来的脚本,99% 情况下缺东西——比如触发器没导、外键顺序错导致导入失败、时间戳时区丢失。
容易踩的坑:mysqldump --no-create-info 导出来只有数据,没 CREATE TABLE;恢复前忘了手动建表,直接执行就报 Table 'xxx' doesn't exist。
- 导出单表结构 + 数据(含创建语句):
mysqldump -u root db_name table_name > backup.sql - PostgreSQL 全量备份并压缩:
pg_dump -U postgres -F c -b -v -f backup.dump db_name(-F c是自定义格式,支持并行恢复) - 恢复时注意权限:MySQL 导入前确保目标库存在;PostgreSQL
pg_restore需指定-d数据库名,不能只给文件
事情说清了就结束。真正难的从来不是“怎么连两张表”,而是搞懂哪部分该由工具兜底、哪部分得自己控制精度,以及——什么时候其实根本不需要 JOIN。