不能。returning 子句需显式列出 rowid 并绑定 urowid 类型变量才能获取,否则报 ora-00904;它提供原子性物理定位,比先查后更新更高效且安全。
returning 子句能直接拿到 rowid 吗?
不能。oracle 的 returning 子句支持返回列值(包括伪列如 rowid),但前提是该伪列在 dml 语句中“可见”且被显式列出——rowid 不是普通列,必须写进 returning 列表才能捕获。
常见错误是只写 UPDATE ... RETURNING id INTO :id,却期望顺带拿到 ROWID;结果变量没声明、绑定失败,或运行时报 ORA-00904: "ROWID": invalid identifier——不是语法错,是没把它当返回项声明。
- 必须显式写
RETURNING ROWID INTO :rid,且宿主变量类型得是UROWID(PL/SQL)或对应数据库驱动的 rowid 类型(如 Python cx_Oracle 的cx_Oracle.ROWID) - 单行更新时,
ROWID是最轻量级的物理定位符,比查主键再二次查询快一个 I/O - 注意:
ROWID在表移动、分区拆分、段压缩后会失效,仅适合“更新后立刻用”的短生命周期场景
UPDATE ... RETURNING ROWID 的最小可行写法
以 PL/SQL 块为例,省略异常处理和变量声明细节,聚焦核心结构:
DECLARE
v_rid UROWID;
BEGIN
UPDATE employees SET salary = salary * 1.1
WHERE employee_id = 100
RETURNING ROWID INTO v_rid;
DBMS_OUTPUT.PUT_LINE('New ROWID: ' || v_rid);
END;
关键点:
-
RETURNING ROWID必须紧接在WHERE条件后,不能加AND或子查询干扰行数判断 - 如果
WHERE匹配 0 行,v_rid保持 NULL,不会报错;匹配多行则抛ORA-01422: exact fetch returns more than requested number of rows - 想安全处理多行?改用
BULK COLLECT INTO+RETURNING ROWID,但那就不是“单行”场景了
绑定变量传参时 ROWID 类型容易踩的坑
用 JDBC、Python 或 Node.js 调用时,ROWID 不是字符串——驱动通常要求显式指定类型,否则可能截断或解析失败。
- Java JDBC:用
PreparedStatement.setObject(idx, null, Types.OTHER)配合ResultSet.getObject(),或直接用 Oracle 扩展类型oracle.jdbc.OracleTypes.ROWID - Python cx_Oracle:绑定前需
cursor.var(cx_Oracle.ROWID),不能用str或bytes变量接收 - Node.js oracledb:
outBinds中设type: oracledb.ROWID,否则返回空字符串 - 错误现象:返回值为空、乱码、或报
ORA-06502: PL/SQL: numeric or value error
为什么不用 SELECT ROWID FROM ... WHERE ... 代替 RETURNING?
看似等价,实则多一次逻辑读,还可能因并发更新导致定位错行。
-
UPDATE ... RETURNING是原子操作:锁住行 → 修改 → 返回物理地址,全程在同一个一致性读 SCN 下 - 分开写
SELECT ROWID再UPDATE,中间若有其他事务提交,WHERE条件可能已不成立,或者更新了错误的行 - 性能上,RETURNING 省掉一次索引/表访问,尤其对大表高频更新场景,延迟敏感时差异明显
- 注意:RETURNING 无法用于 INSERT ALL 或 MERGE,那些场景得另寻方案
ROWID 是 Oracle 底层地址,不是业务标识。拿到它之后别存、别日志、别跨事务用——只要过了这次 UPDATE,它就只对“立刻接下来的物理操作”有意义。










