insert into select 无数据主因是隐式过滤、字段错位、主键冲突或超时;需检查null值、显式列名、添加on duplicate key update或分批处理。

INSERT INTO SELECT 语法写对了但没数据?检查 WHERE 条件和 NULL 过滤
常见现象是执行 INSERT INTO t1 SELECT * FROM t2 后,t1 行数没变。问题往往不在语法,而在隐式过滤:如果 t1 有 NOT NULL 字段且 t2 对应列含 NULL,而目标表又启用了严格 SQL 模式(如 MySQL 的 STRICT_TRANS_TABLES),整条语句会直接报错中止——但有些客户端默认不显示错误,只提示“0 rows affected”。
实操建议:
- 先用
SELECT COUNT(*) FROM t2 WHERE col_x IS NULL检查源表关键字段是否为空 - 临时关闭严格模式测试(MySQL):
SET sql_mode = '';,确认是否为约束拦截 - 显式列出字段,避开有问题的列或用
COALESCE(col_x, 'default')做兜底
字段顺序不一致导致列值错位?必须显式指定字段名
即使两个表结构“相同”,只要字段定义顺序不同(比如 CREATE TABLE a (id INT, name VARCHAR) 和 CREATE TABLE b (name VARCHAR, id INT)),直接 SELECT * 就会让 id 插入到 name 列,造成类型错误或脏数据。
实操建议:
- 永远不用
SELECT *,改用INSERT INTO t1 (id, name, created_at) SELECT id, name, created_at FROM t2 - 用
DESCRIBE t1和DESCRIBE t2对比字段顺序和类型,尤其注意TIMESTAMP与DATETIME、VARCHAR(255)与TEXT等隐式不兼容类型 - 若字段多,可用脚本生成字段列表(例如 Python +
cursor.execute("SHOW COLUMNS FROM t1"))
主键冲突或唯一索引重复?ON DUPLICATE KEY UPDATE 不是万能解
INSERT INTO ... SELECT 遇到主键/唯一索引冲突,默认直接报错(Duplicate entry 'x' for key 'PRIMARY')。加 ON DUPLICATE KEY UPDATE 能续跑,但要注意它只对“冲突行”生效,且更新逻辑需明确覆盖哪些字段。
实操建议:
- 若想跳过重复行,用
INSERT IGNORE INTO ... SELECT(MySQL),但注意它会静默忽略所有错误,包括非主键类错误 - 若要更新已有记录,
ON DUPLICATE KEY UPDATE name = VALUES(name), updated_at = NOW()中的VALUES(name)指的是SELECT子句里对应位置的值,不是目标表原值 - PostgreSQL 用
ON CONFLICT DO UPDATE,语法差异大,不能直接套用 MySQL 写法
大数据量同步卡死或超时?拆分 + 限流 + 事务控制
一次性同步百万级数据容易触发锁表、日志暴涨、连接超时(如 MySQL 的 wait_timeout 或 max_allowed_packet)。单纯加大超时参数治标不治本。
实操建议:
- 按主键分片:用
WHERE id BETWEEN ? AND ?分批插入,每批 1k–5k 行 - 每次插入后加
COMMIT(避免长事务拖慢 binlog 和锁),但注意频繁提交会影响性能,需权衡 - 在从库同步场景下,优先考虑
mysqldump --where="..."或逻辑复制工具(如 pt-archiver),而非手写 INSERT
真正难的不是写一条 INSERT,而是判断该不该全量覆盖、要不要保留历史、冲突时以哪边为准——这些业务逻辑一旦定错,回滚成本远高于重跑语句。










