RETURNING INTO 必须嵌入INSERT/UPDATE/DELETE语句中,不支持独立执行或SELECT;仅适用于单行DML,多行需BULK COLLECT;目标列须为真实列,类型须严格匹配,且必须在PL/SQL块中使用。
Oracle 中 RETURNING INTO 必须配合 DML 使用,不能单独执行
很多人试过写 returning into 却报错 ora-00933: sql command not properly ended,是因为它不是独立语句,必须嵌在 update、delete 或 insert 里。单独写 select ... returning ... 会直接失败。
实操建议:
-
RETURNING INTO只支持单行 DML(即一次最多影响一行),否则会报ORA-01422: exact fetch returns more than requested number of rows - 若需多行返回,得用
RETURNING BULK COLLECT INTO+ PL/SQL 集合变量,不能用普通标量变量 - 目标列必须是表中真实存在的列,不能是表达式或别名,比如
RETURNING salary * 1.1 INTO :new_sal是非法的
UPDATE + RETURNING INTO 获取更新后值的典型写法
常见场景:更新用户状态后立刻拿到新生成的 updated_at 时间戳或自增字段,避免再查一次。
示例(PL/SQL 块内):
DECLARE
v_new_time TIMESTAMP;
BEGIN
UPDATE users
SET status = 'active', updated_at = SYSTIMESTAMP
WHERE id = 123
RETURNING updated_at INTO v_new_time;
DBMS_OUTPUT.PUT_LINE('更新后时间:' || v_new_time);
END;注意点:
- 变量
v_new_time类型必须和updated_at列完全一致(比如TIMESTAMP,不能用DATE代替) - 如果
WHERE条件没匹配到任何行,RETURNING INTO不报错,但变量保持初始值(NULL),需手动检查SQL%ROWCOUNT - 不能在纯 SQL 工具(如 SQL Developer 的“运行脚本”模式)里直接执行带
RETURNING INTO的语句,必须放在 PL/SQL 块中
INSERT + RETURNING INTO 拿主键或默认值
最常用在插入后立即获取数据库生成的 ID(比如 IDENTITY 列或序列值),比先 SELECT seq.NEXTVAL 再插更安全。
示例:
DECLARE
v_id NUMBER;
BEGIN
INSERT INTO orders (product, amount)
VALUES ('book', 99.9)
RETURNING id INTO v_id;
DBMS_OUTPUT.PUT_LINE('新订单ID:' || v_id);
END;关键细节:
- 如果表用的是
GENERATED ALWAYS AS IDENTITY,RETURNING id INTO能直接拿到值;但若用触发器 + 序列,必须确保触发器在INSERT后赋值,否则返回 NULL -
RETURNING可同时取多个列,用逗号分隔:RETURNING id, created_at INTO v_id, v_time,对应变量顺序必须严格一致 - 不支持在
INSERT ... SELECT语句中使用RETURNING(会报ORA-00933)
DELETE + RETURNING INTO 安全删前留痕
适合需要在删除前记录被删数据的场景,比如审计日志、软删除前备份。
示例:
DECLARE
v_name VARCHAR2(100);
v_email VARCHAR2(100);
BEGIN
DELETE FROM contacts
WHERE id = 456
RETURNING name, email INTO v_name, v_email;
INSERT INTO deleted_contacts (name, email, deleted_at)
VALUES (v_name, v_email, SYSTIMESTAMP);
END;容易忽略的限制:
-
RETURNING取的是「删除前」的列值,所以能拿到完整旧数据 - 不能对视图使用
RETURNING,除非该视图可更新且底层映射明确(多数情况不支持) - 如果表有
ON DELETE CASCADE关联子表,RETURNING仍只返回主表字段,不会自动带出子表数据
Oracle 的 RETURNING INTO 看似简单,但类型匹配、单行约束、PL/SQL 上下文这三点,几乎覆盖了所有实际报错原因。尤其容易在迁移 MySQL 或 PostgreSQL 习惯时,默认它像 INSERT ... RETURNING 那样可直接在 SQL 环境运行——它不行。

















