consulta Oracle usando 'como' em coluna de número indexado, o mau desempenho
Pergunta
Na Consulta 1 uma varredura completa da tabela está sendo executada mesmo que o ID é uma coluna indexada. Consulta 2 obtém o mesmo resultado, mas muito mais rápido. Se Consulta 1 é executado retornar uma coluna indexada, em seguida, ele retorna rapidamente, mas se colunas não-indexados são devolvidos ou toda a linha é, então, a consulta demora mais tempo.
Na Consulta 3 corre rápido, mas o 'código' coluna é um VARCHAR2 (10) em vez de um número (12) e está indexada da mesma forma que 'id'.
Por que a Consulta 1 não pegar que ele deve usar o índice? Existe algo que deve ser alterada para permitir colunas de números indexados para executar mais rápido?
[Consulta 1]
select a1.*
from people a1
where a1.id like '119%'
and rownum < 5
Explicar Plano
Select ALL_ROWS
Custo: 67 Bytes: 2.592 Cardinality: 4
2 COUNT STOPKEY
1 QUADRO DE ACESSO completa Tabela pessoas
Custo: 67 Bytes: 3.240 cardinalidade: 5
[Consulta 2]
select a1.*
from people a1, people a2
where a1.id = a2.id
and a2.id like '119%'
and rownum < 5
Explicar Plano
Select ALL_ROWS
Custo: 11 Bytes: 2.620 Cardinality: 4
5 COUNT STOPKEY
4 QUADRO DE ACESSO POR ROWID INDEX Mesa Pessoas
Custo: 3 Bytes: 648 Cardinality: 1 |
3 Nested Loops
Custo: 11 Bytes: 2.620 Cardinality: 4
1 ÍNDICE jejum completo INDEX SCAN people_IDX3
Custo: 2 Bytes: 54.796 Cardinality: 7.828
2 ÍNDICE GAMA SCAN INDEX people_IDX3
Custo: 2 Cardinality: 1
[Consulta 3]
select a1.*
from people a1
where a1.code like '119%'
and rownum < 5
Explicar Plano
Select ALL_ROWS
Custo: 6 Bytes: 1.296 Cardinality: 2
3 COUNT STOPKEY
2 tabela de acesso por índice ROWID Mesa Pessoas
Custo: 6 Bytes: 1.296 Cardinality: 2
1 INDEX RANGE SCAN INDEX people_IDX4
Custo: 3 Cardinality: 2
Solução
COMO condição de correspondência de padrões espera ver os tipos de caracteres como tanto do lado esquerdo e do lado direito operandos. Quando se encontra um número, ele converte-lo implicitamente para Char. Sua Consulta 1 é basicamente silenciosamente reescrito para isso:
SELECT a1.*
FROM people a1
WHERE TO_CHAR(a1.id) LIKE '119%'
AND ROWNUM < 5
Isso acontece no seu caso, e isso é ruim para 2 motivos:
- A conversão é executada para cada linha, que é lento;
- Por causa de uma função (embora implícito) num ONDE predicado, Oracle é incapaz de usar o índice em coluna
A1.ID
.
Para contornar o problema, você precisa fazer uma das seguintes opções:
-
Criar um baseado em função do índice na coluna
A1.ID
:CREATE INDEX people_idx5 ON people (TO_CHAR(id));
-
Se você precisa combinar registros em 3 primeiros caracteres de coluna ID, criar outra coluna do tipo NÚMERO contendo apenas esses 3 caracteres e usar uma planície = operador nele.
-
Criar um
ID_CHAR
separado coluna do tipoVARCHAR2
e preenchê-lo comTO_CHAR(id)
. Indexá-lo e usar em vez deID
em sua condiçãoWHERE
.É claro que se você optar por criar uma coluna adicional com base na coluna de ID existente, você precisa manter os 2 synchronized.You pode fazer isso em batch como um único UPDATE, ou em um gatilho ON-UPDATE, ou adicionar essa coluna para as instruções INSERT e UPDATE apropriadas em seu código.
Outras dicas
COMO é uma função string, então um índice numérico não pode ser usado como facilmente. No índice numérico, você terá 119120130, .., 1191,1192,1193 ..., 11921,11922 ... etc. Isso é todas as linhas que começam com o '119' não será no mesmo lugar, assim todo o índice tem de ser lido (daí o jejum completo SCAN). Em um índice baseado em caracteres que eles estarão juntos (por exemplo, '119', '1191', '11911', '120', ...) para uma melhor gama SCAN pode ser usado.
Se você estava procurando valores id em um determinado intervalo (por exemplo 119000-119999), em seguida, especificar que, como o predicado (id entre 119000 e 119999).
Optimizer decidiu que é mais rápido para fazer uma varredura da tabela, provavelmente devido ao baixo número de registros reais.
Além disso, você deve saber que a correspondência não-exata é sempre muito pior do que exata. Se o seu, onde foi "a1.id = '123456'", seria provavelmente usar o índice. Mas, novamente, mesmo índice leva duas leituras (primeiro encontrar um registro no índice, em seguida, ler o bloco de tabela) e por muito pequenas mesas que poderia decidir por varredura da tabela.
Tente colocar um toque em uma das suas consultas para forçá-lo a usar o índice desejado e, em seguida, verificar o seu plano: pode ser que (devido à inclinação ou o que for) a optimzer não levar o índice em conta, mas decide contra a usá-lo por causa do custo percebido.
A palavra-chave LIKE
diz SQL que você está fazendo uma correspondência de expressão regular. Você nunca deve usar expressões regulares em SQL ou em qualquer biblioteca de programação até ter verificado as funções de cadeia disponíveis para ver se a consulta pode ser expressa simplesmente com eles. Neste caso, você pode mudar isso para um é igual a condição comparando apenas o substring que consiste nos 3 primeiros caracteres do código. No Oracle, este seria parecido com:
SELECT *
FROM people
WHERE SUBSTR(code,1,3) = '119'