Oracle like语句未使用正确的索引

时间:2018-07-12 06:25:23

标签: sql oracle performance query-optimization

Oracle数据库。

我有以下一段SQL对PROVIDER P1表执行全表扫描。我相信这是因为它正在动态建立一个like子句,就像您在XXX行看到的一样。

我在PROVIDER.TERMINAL_NUMBER上有一个索引,并且以下SQL代码段确实使用了正确的索引。

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

2 个答案:

答案 0 :(得分:1)

这里...

 select * from providers where terminal_number like '1234%'

...优化器知道所有配件编号均以固定前缀开头,因此将被共置在索引中。因此,读取索引可能非常有效。

但是这里没有这样的知识...

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

如果终端号码的长度是恒定的,这将起作用。只有您知道您的数据,因此只有您可以知道它是否会飞行。

答案 1 :(得分:0)

如果此查询不使用索引:

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%'