在查询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
答案 0 :(得分:13)
LIKE pattern-matching condition期望将字符类型视为左侧和右侧操作数。当遇到NUMBER时,它会隐式将其转换为char。您的查询1基本上是默默地重写为:
SELECT a1.*
FROM people a1
WHERE TO_CHAR(a1.id) LIKE '119%'
AND ROWNUM < 5
在你的情况下会发生这种情况,这有两个原因:
A1.ID
列上的索引。要解决此问题,您需要执行以下操作之一:
在A1.ID
列上创建function-based index:
CREATE INDEX people_idx5 ON people (TO_CHAR(id));
如果您需要匹配ID列的前3个字符的记录,请创建另一个NUMBER类型的列,其中只包含这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'