Oracle 性能优化:反转思维,让 like’%…’ 查询也能用上索引!17认证网

正规官方授权
更专业・更权威

Oracle 性能优化:反转思维,让 like’%…’ 查询也能用上索引!

作为一名DBA,我们经常会被灌输一条金科玉律:LIKE 查询的百分号 % 不能放在开头,否则索引会失效

比如 WHERE name LIKE 'QYT%' 可以愉快地走索引扫描,而 WHERE name LIKE '%QYT' 就只能退化成全表扫描,在数据量大的时候简直是性能灾难。

这个原理很简单,B-Tree 索引是按从左到右的顺序排序的,你把 % 放前面,等于告诉 Oracle “开头是啥我不知道”,它自然就没法用索引快速定位了。

但规则是死的,人是活的。难道面对 LIKE '%QYT' 这种需求,我们真的就束手无策,只能任由它全表扫描吗?

今天,我们就来分享一个脑洞大开的“骚操作”,利用 Oracle 的函数索引,让这种“废柴”查询也能重新用上索引,起死回生!

实验场景

我们先来搭建一个简单的测试环境。

-- 创建测试表
CREATE TABLE t AS SELECT * FROM dba_objects WHERE object_id IS NOT NULL;
-- 插入两条特征明显的测试数据
UPDATE t SET object_name='ZZZQYT' WHERE object_id=8;UPDATE t SET object_name='QYTZZZ' WHERE object_id=10;COMMIT;

— 在 object_name 列上创建一个常规的 B-Tree 索引

CREATE INDEX idx_object_name ON t(object_name);

情况一:%在后面(常规操作,走索引)

我们先看看标准的好学生是什么样的。

SELECT object_name, object_id FROM t WHERE object_name LIKE 'QYT%';OBJECT_NAME                    OBJECT_ID------------------------------ ----------QYTZZZ                         10
Execution Plan----------------------------------------------------------Plan hash value603483963-------------------------------------------------------------------------------------------------------| Id  | Operation                           | Name            | Rows  | Bytes | Cost (%CPU)| Time     |-------------------------------------------------------------------------------------------------------|   0 | SELECT STATEMENT                    |                 |     1 |    40 |     4   (0)| 00:00:01 ||   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| T               |     1 |    40 |     4   (0)| 00:00:01 ||* 2  |   INDEX RANGE SCAN                  | IDX_OBJECT_NAME |     1 |       |     3   (0)| 00:00:01 |-------------------------------------------------------------------------------------------------------Predicate Information (identified by operation id):---------------------------------------------------
   2 - access("OBJECT_NAME" LIKE 'QYT%')       filter("OBJECT_NAME" LIKE 'QYT%')

Statistics----------------------------------------------------------          1  recursive calls          0  db block gets          5  consistent gets          2  physical reads          0  redo size        639  bytes sent via SQL*Net to client        427  bytes received via SQL*Net from client          2  SQL*Net roundtrips to/from client          0  sorts (memory)          0  sorts (disk)          1  rows processed

毫无悬念,走了INDEX RANGE SCAN,性能杠杠的。

情况二:%在前面(索引失效,全表扫描)

接下来看看坏学生。

SELECT object_name, object_id FROM t WHERE object_name LIKE '%QYT';OBJECT_NAME                    OBJECT_ID------------------------------ ----------ZZZQYT                         8
Execution Plan----------------------------------------------------------Plan hash value1601196873
--------------------------------------------------------------------------| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |--------------------------------------------------------------------------|   0 | SELECT STATEMENT  |      |  3640 |   142K|   394   (1)| 00:00:01 ||*  1 |  TABLE ACCESS FULL| T    |  3640 |   142K|   394   (1)| 00:00:01 |--------------------------------------------------------------------------
Predicate Information (identified by operation id):---------------------------------------------------
   1 - filter("OBJECT_NAME" LIKE '%QYT' AND "OBJECT_NAME" IS NOT NULL)

Statistics----------------------------------------------------------          1  recursive calls          0  db block gets       1419  consistent gets          0  physical reads          0  redo size        639  bytes sent via SQL*Net to client        648  bytes received via SQL*Net from client          2  SQL*Net roundtrips to/from client          0  sorts (memory)          0  sorts (disk)          1  rows processed

同样不出所料,Oracle放弃了索引,选择了TABLE ACCESS FULL,逻辑读飙升。

奇思妙想:反转一下,世界大不同!

既然从左往右走不通,那我们能不能让它从右往左走呢?

答案是可以的!借助 Oracle 的REVERSE函数。这个函数可以把一个字符串完全颠倒过来。

SELECT REVERSE('%QYT'FROM dual;REVE----TYQ%

看到了吗? %QYT 被反转成了 TYQ% !那个讨厌的、挡在前面的%,一下子跑到了最后面,这不就又变成了索引最喜欢的格式了吗?

有了这个思路,我们的优化方案就来了:

1. 创建一个反向函数索引:

我们在object_name列上,创建一个基于REVERSE函数的索引。

CREATE INDEX idx_reverse_objname ON t(REVERSE(object_name));

这个索引里存储的不再是ZZZQYT,而是TYQZZZ。

2. 改造我们的 SQL 查询:

在写查询的时候,把WHERE条件也用REVERSE函数包起来。

SELECT object_name, object_idFROM tWHERE REVERSE(object_name) LIKE REVERSE('%QYT'); -- 等价于 WHERE REVERSE(object_name) LIKE 'TYQ%'
OBJECT_NAME                    OBJECT_ID------------------------------ ----------ZZZQYT                         8

现在,让我们再来看看这条“改造”后 SQL 的执行计划:

执行计划:

Execution Plan----------------------------------------------------------Plan hash value944999405
-----------------------------------------------------------------------------------------------------------| Id  | Operation                           | Name                | Rows  | Bytes | Cost (%CPU)| Time     |-----------------------------------------------------------------------------------------------------------|   0 | SELECT STATEMENT                    |                     |  3640 |   376K|   392   (0)| 00:00:01 ||   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| T                   |  3640 |   376K|   392   (0)| 00:00:01 ||*  2 |   INDEX RANGE SCAN                  | IDX_REVERSE_OBJNAME |   655 |       |     7   (0)| 00:00:01 |-----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):---------------------------------------------------
   2 - access(REVERSE("OBJECT_NAME") LIKE 'TYQ%')       filter(REVERSE("OBJECT_NAME") LIKE 'TYQ%')

Statistics----------------------------------------------------------          2  recursive calls          0  db block gets          7  consistent gets          7  physical reads          0  redo size        639  bytes sent via SQL*Net to client        445  bytes received via SQL*Net from client          2  SQL*Net roundtrips to/from client          0  sorts (memory)          0  sorts (disk)          1  rows processed

奇迹发生了!Oracle聪明地使用了我们创建的反向函数索引IDX_REVERSE_OBJNAME,执行了INDEX RANGE SCAN路径。对比一下全表扫描的统计信息,逻辑读从1419骤降到了7!

通过 REVERSE 函数和函数索引的组合,我们成功地绕过了LIKE查询对前导 % 的限制,把一个原本无法使用索引的查询,变成了一个高效的索引扫描操作。

这个方法虽然看起来有点“曲线救国”,但在某些特定场景下,不失为一种实用且有效的性能优化技巧。

未经允许不得转载:17认证网 » Oracle 性能优化:反转思维,让 like’%…’ 查询也能用上索引!
分享到:0

评论已关闭。

400-663-6632
咨询老师
咨询老师
咨询老师