Oracle查询在索引编号列上使用'like',性能不佳

时间:2009-11-04 19:31:34

标签: sql oracle indexing oracle10g sql-like

在查询1上,即使id是索引列,也正在执行全表扫描。查询2实现了相同的结果,但速度更快。如果运行查询1返回索引列,则它会快速返回,但如果返回非索引列或整行,则查询需要更长时间。

在查询3中,它运行得很快但是“代码”列是VARCHAR2(10)而不是NUMBER(12),其索引方式与“id”相同。

为什么查询1没有意识到它应该使用索引?是否应该更改某些内容以允许索引编号列更快地执行?

[查询1]

select a1.*
from people a1
where a1.id like '119%' 
and rownum < 5

解释计划
SELECT STATEMENT ALL_ROWS
费用:67字节:2,592基数:4
    2 COUNT STOPKEY
1表访问全表人 费用:67字节:3,240基数:5

[查询2]

select a1.*
from people a1, people a2
where a1.id = a2.id
and a2.id like '119%' 
and rownum < 5

解释计划
SELECT STATEMENT ALL_ROWS
费用:11字节:2,620基数:4
5 COUNT STOPKEY
4表索引按ROWID表人员
费用:3字节:648基数:1
3嵌套的车 费用:11字节:2,620基数:4
1 INDEX FAST FULL SCAN INDEX people_IDX3
费用:2字节:54,796基数:7,828
2 INDEX RANGE SCAN INDEX people_IDX3
费用:2基数:1

[查询3]

select a1.*
from people a1
where a1.code like '119%' 
and rownum < 5

解释计划
SELECT STATEMENT ALL_ROWS
费用:6字节:1,296基数:2
3 COUNT STOPKEY
2通过INDEX ROWID TABLE人员的表访问 费用:6字节:1,296基数:2
1 INDEX RANGE SCAN INDEX people_IDX4
费用:3基数:2

5 个答案:

答案 0 :(得分:13)

LIKE pattern-matching condition期望将字符类型视为左侧和右侧操作数。当遇到NUMBER时,它会隐式将其转换为char。您的查询1基本上是默默地重写为:

SELECT a1.*
  FROM people a1
 WHERE TO_CHAR(a1.id) LIKE '119%'
   AND ROWNUM < 5

在你的情况下会发生这种情况,这有两个原因:

  1. 对每一行执行转换,这很慢;
  2. 由于WHERE谓词中的函数(虽然是隐式),Oracle无法使用A1.ID列上的索引。
  3. 要解决此问题,您需要执行以下操作之一:

    1. A1.ID列上创建function-based index

      CREATE INDEX people_idx5 ON people (TO_CHAR(id));

    2. 如果您需要匹配ID列的前3个字符的记录,请创建另一个NUMBER类型的列,其中只包含这3个字符,并在其上使用普通的 = 运算符。

    3. 创建ID_CHAR类型的单独VARCHAR2,并将其填入TO_CHAR(id)。将其编入索引并在ID条件中使用而不是WHERE

      当然,如果您选择基于现有ID列创建其他列,则需要将这两个列保持同步。您可以批量执行此操作作为单个UPDATE,或在ON-UPDATE触发器中,或添加该列代码中适当的INSERT和UPDATE语句。

答案 1 :(得分:4)

LIKE是一个字符串函数,因此不能轻易使用数字索引。在数字索引中,你将有119,120,130,...,1191,1192,1193 ......,11921,11922 ...等等。这是以'119'开头的所有行都不会在同一个地方,所以必须读取整个索引(因此快速全扫描)。在基于字符的索引中,它们将在一起(例如'119','1191','11911','120',...),因此可以使用更好的RANGE SCAN。

如果您要查找特定范围内的id值(例如119000到119999),请将其指定为谓词(id在119000和119999之间)。

答案 2 :(得分:1)

优化器决定进行表扫描更快,很可能是由于实际记录数量较少。

另外,你应该知道非精确匹配总是比精确匹配更糟糕。如果你的位置是“a1.id ='123456'”,它很可能会使用索引。但话说再说一遍,即使索引需要两次读取(首先在索引中查找记录,然后从表中读取块),对于非常小的表,它可以决定是否进行表扫描。

答案 3 :(得分:0)

尝试在您的一个查询中添加提示以强制它使用所需的索引,然后检查您的计划:可能是(由于倾斜或其他)优化器确实获取索引考虑到了,但由于感知成本而决定不使用它。

答案 4 :(得分:-3)

LIKE关键字告诉SQL您正在进行正则表达式匹配。在检查可用的字符串函数之前,不要在SQL或任何编程库中使用正则表达式,以查看是否可以使用它们简单地表达查询。在这种情况下,您可以通过仅比较由代码的前3个字符组成的子字符串将其更改为等于条件。在Oracle中,这看起来像:

SELECT *
FROM people
WHERE SUBSTR(code,1,3) = '119'