不能。insert all 是单条sql内对同一数据集多路分发插入,非并发写入,不保证跨表原子性,本质为一次解析、多次执行,各into子句共享源数据结构且须列数类型兼容。

INSERT ALL 能否真正“同时”写入多个表?
不能。Oracle 的 INSERT ALL 是单条 SQL 语句内对同一输入数据集做**多路分发插入**,不是并发写入,也不保证原子性跨表(除非整个语句成功或全部回滚)。它本质是“一次解析、多次执行”,目标表之间无事务隔离差异,但每条 INTO 子句仍是独立的 DML 操作。
常见误解是以为它像存储过程里开多个 INSERT 那样灵活——实际它要求所有 INTO 子句共享相同的源数据结构(比如都从 SELECT 或 VALUES 获取列),且不能对不同表用不同条件逻辑混用字段别名。
基本语法与必须遵守的约束
INSERT ALL 必须以 INSERT ALL 开头,后接一个或多个 INTO table_name [(col1, col2, ...)] VALUES (val1, val2, ...),最后以 SELECT ... FROM dual 或带数据的子查询收尾。
- 所有
INTO子句的VALUES列数、类型必须与目标表对应列兼容(隐式转换有风险) - 不能在
VALUES中直接引用其他INTO子句的别名或计算结果 - 若用
SELECT作为源,所有INTO子句共享该查询输出的每一行;无法为不同表指定不同行过滤条件(想实现需用INSERT FIRST+WHEN) - 主键/唯一约束冲突会导致整条
INSERT ALL失败,不会只跳过某一张表的那条记录
示例(安全写法):
INSERT ALL INTO orders (order_id, cust_id, order_date) VALUES (1001, 123, SYSDATE) INTO order_items (item_id, order_id, prod_id, qty) VALUES (2001, 1001, 555, 2) SELECT 1 FROM dual;
想按条件分发到不同表?用 INSERT FIRST + WHEN
当需要根据数据内容决定插入哪张表(比如状态为 'A' 插 A 表,'B' 插 B 表),INSERT ALL 不够用,得换 INSERT FIRST。
它会逐条检查 WHEN 条件,命中第一个为 TRUE 的就插入对应表,其余跳过——这才是“条件路由”。注意:它不支持 VALUES,必须搭配 SELECT 源。
-
WHEN后只能跟布尔表达式,不能调用 PL/SQL 函数(除非定义为DETERMINISTIC) - 所有
WHEN分支共享同一个SELECT输出行,字段名需在SELECT中显式提供 - 没匹配任何
WHEN的行会被丢弃(可加ELSE分支兜底)
示例:
INSERT FIRST
WHEN status = 'NEW' THEN
INTO customers_new (id, name, created) VALUES (id, name, SYSDATE)
WHEN status = 'ARCHIVED' THEN
INTO customers_archive (id, name, archived) VALUES (id, name, SYSDATE)
ELSE
INTO customers_error (id, name, error_msg) VALUES (id, name, 'Unknown status')
SELECT 101 id, 'Alice' name, 'NEW' status FROM dual;
性能和事务边界要注意什么?
INSERT ALL 和 INSERT FIRST 都属于单条 SQL,在事务中表现为一个 DML 单元:要么全部成功,要么全部回滚。但这不等于高效率——尤其当目标表有大量索引、触发器或外键约束时,每条 INTO 实际仍会单独走一遍约束检查和索引维护流程。
- 如果各表数据量差异大(比如一张插 1 行,另一张插 10000 行),不如拆成独立
INSERT并手动控制批量提交 - 避免在
INSERT ALL中对同一张表多次INTO(语法允许但毫无意义,还增加解析开销) - 使用绑定变量时,
VALUES子句里的参数必须一一对应;若用SELECT,绑定变量要放在SELECT子句里,不能分散在各INTO
最容易被忽略的是:没有 SELECT 就无法执行——哪怕只是 SELECT 1 FROM dual,漏掉这句会报 ORA-00928: missing SELECT keyword。











