一次INSERT插入多行数据的正确写法是单条语句、多组圆括号VALUES用英文逗号分隔,字段顺序与数量须严格一致,主流数据库均支持,Oracle旧版本除外。

INSERT INTO 语句完全支持一次插入多行,只要用对语法——不是写多个 INSERT,也不是拼 UNION ALL,而是单条语句、多组 VALUES。
VALUES 后面跟多组圆括号,用英文逗号分隔
这是最核心的写法,所有主流数据库(MySQL、PostgreSQL、SQL Server、SQLite)都原生支持,Oracle 除外(12c 及以后版本才部分支持,旧版必须拆成多条语句)。
常见错误是把下面这种写法当成合法 SQL:
INSERT INTO users (name, email) VALUES ('Alice', 'a@example.com');
INSERT INTO users (name, email) VALUES ('Bob', 'b@example.com');
这其实是两条独立语句,在多数客户端或 ORM 中会因分号或换行被截断,只执行第一行;在严格模式下直接报错。
- 正确写法:所有值组写在同一个
VALUES后,每组用()包裹,组间用英文逗号连接 - 列名列表只出现一次,必须与每组值的顺序、数量、类型严格对应
- 字符串要用单引号,数字不加引号,
NULL直接写,不要加引号
PostgreSQL 对类型和长度更敏感
PostgreSQL 在多行 INSERT 中会逐行校验类型,比如你写 (1, '2024-01-01'), (2, 'invalid-date'),整条语句会失败,哪怕第一行完全合法。
MySQL 相对宽松,可能只警告或隐式转换(如把字符串转为数字),但后果不可控。
- 建议显式 cast:比如
TO_DATE('2024-01-01', 'YYYY-MM-DD')(PostgreSQL)或STR_TO_DATE('2024-01-01', '%Y-%m-%d')(MySQL) - 避免混合类型:不要在同列位置混用
'1'和2,即使数值相同 - 单次插入建议 ≤ 1000 行;超过后容易触发内存限制或超时,尤其在远程连接或低配实例上
自增主键和默认值怎么处理
如果表有 id SERIAL(PostgreSQL)或 id INT AUTO_INCREMENT(MySQL),你完全可以省略该列——但必须同步省略所有对应位置的值,并确保列名列表不包含它。
例如:INSERT INTO users (name, email) VALUES ('A', 'a@b.c'), ('B', 'b@b.c'); —— 这里没提 id,数据库自动分配。
- 如果列定义了
DEFAULT(如created_at TIMESTAMP DEFAULT NOW()),也不用写,留空即可 - 但不能写成
(DEFAULT, 'A', 'a@b.c'),除非你明确需要触发默认值计算(少数场景有用) - 如果某列允许
NULL且无默认值,可直接写NULL,不要留空或写空字符串
批量插入性能和事务边界
单条多行 INSERT 比循环执行 N 条单行 INSERT 快 5–10 倍,因为减少了网络往返和解析开销。但它仍是一次 DML 操作,受事务控制。
这意味着:如果插入第 500 行失败,前面 499 行也全部回滚(除非你用了 ON CONFLICT DO NOTHING 或 INSERT IGNORE 等容错语法)。
- 生产环境大批量导入,建议分批(如每 500 行一个事务),避免锁表太久或 WAL 日志暴涨
- 不要把整个 CSV 一次性塞进一条
INSERT——超过 5000 行就该切片 - 注意字符集:如果数据含 emoji 或特殊符号,确保连接层和表字段都是
utf8mb4,否则可能截断或报错
真正容易被忽略的是 Oracle 兼容性——如果你的代码要跑在 Oracle 11g/12c 早期版上,INSERT INTO ... VALUES (...), (...) 是无效语法,必须改用 INSERT ALL 或拆成多条语句。其他数据库基本没这个问题。










