往期回顾:
左右SQL执行计划妙招 ③ ——执行计划SQL写法差异改变【with子句】
左右SQL执行计划妙招 ④ ——执行计划SQL写法差异改变【insert all】
左右SQL执行计划妙招 ⑤ ——执行计划利用设计特性改变【并行度影响】
左右SQL执行计划妙招 ⑥ ——执行计划SQL写法差异改变【rownum分页】
左右SQL执行计划妙招 ⑦——执行计划SQL写法差异改变【rownum实体化】
在SQL性能优化过程中,SQL的写法细节常常对执行计划产生不可忽视的影响。合理利用Oracle数据库的特性,能够让同一业务实现方案下的SQL拥有更高的执行效率。本篇将聚焦于rowid的使用,通过两个实际对比例子,详细剖析不同SQL写法对执行计划的影响,为SQL优化实战提供借鉴。
首先,构建测试环境并初始化数据:
drop table t purge; create table t as select * from dba_objects; update t set object_id = rownum; commit; create index idx_object_id on t(object_id); set autotrace off; select rowid from t where object_id=8;
此时,可以查到object_id=8的数据行对应的rowid,例如:
ROWID ----- AAAaq2AALAAApOrAAH
1. 普通条件更新(语句1)
update t set object_name='abc' where object_id=8; 执行计划: ----------------------------------------------------------------------------------- Plan hash value: 3890322047 ----------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ----------------------------------------------------------------------------------- | 0 | UPDATE STATEMENT | | 1 | 79 | 2 (0)| 00:00:01 | | 1 | UPDATE | T | | | | | |* 2 | INDEX RANGE SCAN| IDX_OBJECT_ID | 1 | 79 | 1 (0)| 00:00:01 | ----------------------------------------------------------------------------------- - dynamic sampling used for this statement (level=2)
统计信息:
30 recursive calls 2 db block gets 75 consistent gets 0 physical reads 448 redo size 680 bytes sent via SQL*Net to client 616 bytes received via SQL*Net from client 3 SQL*Net roundtrips to/from client 1 sorts (memory) 0 sorts (disk) 1 rows processed
该语句的执行计划中,Oracle选择通过idx_object_id索引进行INDEX RANGE SCAN,然后定位数据行并执行更新。
update t set object_name='abc' where object_id=8 and t.rowid='AAAaq2AALAAApOrAAH';
执行计划:
------------------------------------------------------------------------------------
Plan hash value: 815416494
------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------
| 0 | UPDATE STATEMENT | | 1 | 91 | 1 (0)| 00:00:01 |
| 1 | UPDATE | T | | | | |
|* 2 | TABLE ACCESS BY USER ROWID| T | 1 | 91 | 1 (0)| 00:00:01 |
------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
------------------------------------------------------------------------------------
2 - filter("OBJECT_ID"=8)
统计信息:
0 recursive calls
3 db block gets
1 consistent gets
0 physical reads
0 redo size
682 bytes sent via SQL*Net to client
650 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
1 rows processed
在此语句中,加入了rowid作为查询条件。Oracle直接根据rowid访问对应数据行,执行计划变为TABLE ACCESS BY USER ROWID,这一方式被认为是直接、跳跃式访问表中某条记录的最快途径。
从执行计划及统计信息对比可以看出:

语句1 先通过索引查找,再定位到表中的记录,适合object_id存在索引的场景。
语句2 由于rowid已明确,无须再借助索引,可以绕过索引查找的步骤,直接到达物理存储位置。Oracle明确选择TABLE ACCESS BY USER ROWID这一最快的单行定位方式,提升了访问效率。
实际应用中,在已知rowid的情况下,采用rowid作为定位依据,可以减少数据库的消耗,实现最短路径访问。在批量或高并发更新场景下,尤其能展现出rowid方式的优势。
1. ROWID的时效性
- DDL操作影响:表重建、分区操作会改变ROWID
- 数据移动影响:行迁移、行链接可能导致ROWID变化
- 时间窗口:ROWID应在短时间内使用,避免失效
2. 业务逻辑考虑
- 数据一致性:确保ROWID对应的行仍然满足业务条件
- 并发控制:考虑其他会话可能的数据修改
- 错误处理:处理ROWID无效的情况
3. 性能权衡
- 查询开销:获取ROWID本身也需要成本
- 应用复杂度:增加了应用逻辑的复杂性
- 适用范围:并非所有场景都适合使用ROWID
在特定的业务场景下,语句1和语句2在逻辑上完全等价,但性能表现却截然不同。TABLE ACCESS BY USER ROWID是Oracle中最快的访问方式,它通过直接的物理定位避免了索引查找的开销,大幅减少了逻辑读和递归调用。
理解和掌握ROWID的使用技巧,能够在适当场景下显著提升SQL执行效率。然而,ROWID的使用需要谨慎考虑其时效性和业务适用性,在性能优化和代码复杂度之间找到合适的平衡点。

未完待续..
左右SQL执行计划妙招 ⑨ ——执行计划SQL写法差异改变【缓存结果影响】
公众号:收获不止数据库
微信:ljbyxl1213
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/9091385/viewspace-3086445/ ,如需转载,请注明出处,否则将追究法律责任。