INSERT INTO … SELECT … 常见问答(含样例)
1)INSERT INTO A SELECT x FROM B 是什么意思?
回答
把查询 SELECT x FROM B 返回的结果集逐行插入到表 A。插入哪些列,取决于是否写了目标列清单以及列数/顺序是否匹配。
示例
– 把 B.person_id 插到 A.user_id
INSERT INTO A(user_id)
SELECT person_id
FROM B;
2)不写列清单会怎样?列如何对齐?
回答
INSERT INTO A SELECT …(无列清单)要求 SELECT 产生的列数与顺序和 A 的物理列顺序一一对应。列数不等或类型不兼容会报错;即使能成功,也脆弱(A 增减列就歪了),不推荐。
示例
– 不推荐:依赖 A 的物理列顺序
INSERT INTO A
SELECT x, y, z FROM B;
3)A 有 100 列,只插 5 列可以吗?
回答
可以,但必须显式列出这 5 列;其余 95 列要么 允许 NULL、要么有默认值/自动生成,否则会因 NOT NULL 无默认而报错。
示例
– 只写 5 列,其余列走默认值或 NULL
INSERT INTO A(c1, c2, c3, c4, c5)
SELECT x1, x2, x3, x4, x5
FROM B;
4)INSERT … SELECT 没有列清单,如何理解字段对应?
回答
数据库按位置对齐:SELECT 的第 1 列对应 A 的第 1 列……依此类推。
若 SELECT *,就会把星号展开成来源表的全部列,再按位置对齐到 A(极易错位)。最佳实践:永远显式写出 A 的目标列清单。
示例
– 脆弱:A 增列/改顺序就错位
INSERT INTO A
SELECT * FROM B;
5)如何插常量或表达式?
回答
SELECT 子句里可以是列、表达式、常量的任意组合。
示例
INSERT INTO A(user_id, death_date, remark)
SELECT person_id,
CAST(death_dt AS DATE),
‘import-from-B’
FROM B
WHERE death_dt IS NOT NULL;
6)有自增/标识/生成列怎么办?
回答
这类列通常不要出现在目标列清单里,交给数据库自动填。SQL Server 若要手动写入,需先 SET IDENTITY_INSERT A ON。
示例
– MySQL/PG:id 自增/序列自动填
INSERT INTO A(user_id, value)
SELECT person_id, score FROM B;
7)只想插入一行「全部默认值」怎么写?
回答
PostgreSQL / SQL Server:INSERT INTO A DEFAULT VALUES;
MySQL:INSERT INTO A () VALUES ();
8)如何防止重复插入?
回答(三种常用)
约束法:给唯一键/主键,违反时失败(最干净)。
过滤法:NOT EXISTS / LEFT JOIN IS NULL 只插不存在的。
方言法:PG ON CONFLICT DO NOTHING;MySQL INSERT IGNORE 或 ON DUPLICATE KEY UPDATE。
示例(通用)
INSERT INTO A(key_col, val)
SELECT b.key_col, b.val
FROM B b
WHERE NOT EXISTS (
SELECT 1 FROM A a WHERE a.key_col = b.key_col
);
9)为什么会报错「列数不匹配 / 不能为空 / 类型不兼容」?
回答
列数不匹配:没写列清单且 SELECT 列数与 A 列数不同。
不能为空:A 中有 NOT NULL 列既没在列清单里,也无默认值。
类型不兼容:SELECT 的列类型与 A 的目标列类型不匹配,需 CAST/CONVERT。
示例(显式转换)
INSERT INTO A(death_date)
SELECT CAST(death_dt AS DATE)
FROM B;
10)跨库/不同方言的小差异
回答(速览)
PostgreSQL / SQL Server:未指定列 → 用默认值;无默认则 NULL;NOT NULL 无默认会失败。
MySQL:同上;还支持 INSERT INTO A SET c1=…, c2=…;;以及 INSERT INTO A () VALUES (); 插全部默认。
Hive/Spark(宽表常见):更依赖位置对齐,更要写列清单。
11)最佳实践清单(拿去即用)
回答
总是写明目标列清单,并与 SELECT 列一一对应。
对类型不兼容的列显式 CAST。
让自增/生成列自动填,不要手写。
保证未写入的列要么可空,要么有默认值。
防重:唯一键 + NOT EXISTS/ON CONFLICT/ON DUPLICATE KEY。
避免 SELECT *,避免依赖物理列序。
大批量插入用事务与批次(分批提交),必要时关闭/延后索引/触发器再重建。
12)进阶样例:多列映射+去重+类型转换
示例
– 把 B 的数据清洗后插入 A,避免重复,兼顾类型转换
INSERT INTO A(user_id, death_date, death_reason, remark)
SELECT b.person_id,
CAST(b.death_dt AS DATE),
b.death_reason,
CONCAT('import ', CURRENT_DATE)
FROM B b
WHERE b.death_dt IS NOT NULL
AND NOT EXISTS (
SELECT 1 FROM A a
WHERE a.user_id = b.person_id
AND a.death_date = CAST(b.death_dt AS DATE)
);
