Oracle like 语句未使用正确的索引
Posted
技术标签:
【中文标题】Oracle like 语句未使用正确的索引【英文标题】:Oracle like statement not using correct index 【发布时间】:2018-07-12 06:25:22 【问题描述】:Oracle 数据库。
我有以下 SQL 段,它正在对 PROVIDER P1 表执行全表扫描。我相信这是因为它动态地构建了一个like 子句,正如您在第 XXX 行看到的那样。
我在 PROVIDER.TERMINAL_NUMBER 上有一个索引,并且以下 SQL sn-p 确实使用了正确的索引。
select * from providers where terminal_number like '1234%'
那么为什么以下内容没有达到该索引?
SELECT P1.PROVIDER_NUMBER, P1.TERMINAL_NUMBER, PC."ORDER" FROM PROVIDERS P1
INNER JOIN PROVIDER_CONFIG PC
ON PC.PROVIDER_NUMBER = P1.PROVIDER_NUMBER
WHERE EXISTS (
SELECT E2.* FROM EQUIPMENT E1
INNER JOIN EQUIPMENT E2
ON E1.MERCHANT_NUMBER = E2.MERCHANT_NUMBER
WHERE E1.TERMINAL_NUMBER = 'SA323F'
AND E1.STATUS IN (0, 9)
AND E2.STATUS IN (0, 9)
XXX
AND P1.TERMINAL_NUMBER LIKE SUBSTR(E2.TERMINAL_NUMBER, 0, length(E2.TERMINAL_NUMBER) - 1) || '%'
)
ORDER BY PC."ORDER" DESC
【问题讨论】:
“我相信这是因为它动态构建了一个 like 子句” - 事实并非如此。 【参考方案1】:这里...
select * from providers where terminal_number like '1234%'
... Optimiser 知道所有拟合数字都以固定前缀开头,因此将位于索引中。因此,读取索引可能非常有效。
但是这里没有这方面的知识……
P1.TERMINAL_NUMBER LIKE SUBSTR(E2.TERMINAL_NUMBER, 0, length(E2.TERMINAL_NUMBER) - 1) || '%'
E2.TERMINAL_NUMBER
可以有任意数量的不同前缀,查询将返回来自PROVIDERS
表的所有记录。所以索引读取的效率会非常低,而直接的全扫描方法是正确的选择。
可能可以重写查询,使其更有效地工作 - 例如,您需要快速全索引扫描而不是全表扫描。但在不了解您的数据和业务规则的情况下,我们无法真正提供帮助,尤其是在涉及动态查询生成时。
可能会提高性能的一件事是将 WHERE EXISTS 替换为 WHERE IN...
SELECT P1.PROVIDER_NUMBER, P1.TERMINAL_NUMBER, PC."ORDER" FROM PROVIDERS P1
INNER JOIN PROVIDER_CONFIG PC
ON PC.PROVIDER_NUMBER = P1.PROVIDER_NUMBER
WHERE substr(P1.TERMINAL_NUMBER, 1, 5) IN (
SELECT SUBSTR(E2.TERMINAL_NUMBER, 1, 5)
FROM EQUIPMENT E1
INNER JOIN EQUIPMENT E2
ON E1.MERCHANT_NUMBER = E2.MERCHANT_NUMBER
WHERE E1.TERMINAL_NUMBER = 'SA323F'
AND E1.STATUS IN (0, 9)
AND E2.STATUS IN (0, 9)
)
ORDER BY PC."ORDER" DESC
如果终端编号的长度是恒定的,这将起作用。只有你知道你的数据,所以只有你能判断它是否会飞。
【讨论】:
SELECT null
不会使查询比 SELECT e2.*
快一点,这对于 Oracle(和许多其他 DBMS)来说是一个从未出现过的性能神话【参考方案2】:
如果此查询不使用索引:
select *
from providers
where terminal_number like '1234%'
那么大概terminal_number
是数字而不是字符串。类型转换阻止了索引的使用。
如果要使用索引,则将值转换为字符串并使用字符串索引:
create index idx_providers_terminal_number_str on providers(cast(terminal_number as varchar2(255)));
然后将查询写成:
select *
from providers
where cast(terminal_number as varchar2(255)) like '1234%'
【讨论】:
以上是关于Oracle like 语句未使用正确的索引的主要内容,如果未能解决你的问题,请参考以下文章