Tenho a seguinte tabela:
CREATE TABLE MY_DATA (
MY_DATA_ID NUMBER(38,0) PRIMARY KEY,
GEOM SDO_GEOMETRY
);
Configurei seus metadados SDO e criei um índice espacial:
INSERT INTO USER_SDO_GEOM_METADATA (TABLE_NAME, COLUMN_NAME, SRID, DIMINFO)
VALUES ('MY_DATA', 'GEOM', 4326, MDSYS.SDO_DIM_ARRAY(MDSYS.SDO_DIM_ELEMENT('X',-180.0000,180.0000,0.00000000001),MDSYS.SDO_DIM_ELEMENT('Y',-90.0000,90,0.00000000001)));
CREATE INDEX SPIX_MY_DATA ON MY_DATA (GEOM) INDEXTYPE IS MDSYS.SPATIAL_INDEX;
A tabela tem cerca de 150.000 linhas.
Eu preciso encontrar linhas onde as geometrias cruzam uma linha específica. Esta parece ser uma tarefa bastante simples. Tudo o que é preciso é uma JOIN
e uma WHERE
cláusula:
SELECT /*+ INDEX(MD2 SPIX_MY_DATA)*/
MD2.MY_DATA_ID
FROM MY_DATA MD1
JOIN MY_DATA MD2 ON SDO_RELATE(MD1.GEOM, MD2.GEOM, 'mask=ANYINTERACT') = 'TRUE'
WHERE MD1.MY_DATA_ID = 143668 AND MD2.MY_DATA_ID != 143668
Mas esta consulta é muito lenta. Leva mais de 85 segundos para buscar apenas 8 linhas relacionadas. Podemos ver no plano de consulta que ele não está usando o índice espacial, apesar da dica de índice:
Plan hash value: 4008712617
----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 379 | 9854 | 1390 (1)| 00:00:17 |
| 1 | NESTED LOOPS | | 379 | 9854 | 1390 (1)| 00:00:17 |
| 2 | TABLE ACCESS BY INDEX ROWID| MY_DATA | 1 | 13 | 2 (0)| 00:00:01 |
|* 3 | INDEX UNIQUE SCAN | SYS_C00755898 | 1 | | 1 (0)| 00:00:01 |
|* 4 | TABLE ACCESS FULL | MY_DATA | 379 | 4927 | 1388 (1)| 00:00:17 |
----------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("MD1"."MY_DATA_ID"=143668)
4 - filter("MD2"."MY_DATA_ID"<>143668 AND
"MDSYS"."SDO_RTREE_RELATE"("MD1"."GEOM","MD2"."GEOM",'mask=ANYINTERACT
querytype=window ')='TRUE')
(A linha 2 é MD1 e a linha 4 é MD2.)
Eu tentei mudar a dica para /*+ INDEX(MY_DATA SPIX_MY_DATA)*/
, mas isso também não teve efeito. Tenho certeza de que minhas estatísticas estão atualizadas.
Como posso acelerar esta consulta? Como posso obtê-lo para usar o índice espacial?
Atualmente estou usando o Oracle 11.2.
Comportamento
SDO_RELATE
e o planejador de consultasSDO_RELATE
usará apenas o índice espacial do primeiro parâmetro . A documentação não diz isso explicitamente, mas pode ser obtido a partir de alguns detalhes.A documentação para os dois argumentos de geometria diz:
(ênfase minha)
Isso sugere muito fortemente que
SDO_RELATE
é projetado especificamente para usar o índice espacial em seu primeiro argumento . No entanto, como você tem um filtro de chave primáriaMD1
em suaWHERE
cláusula, o planejador conclui corretamente que usar o índice de chave primária será mais rápido do que usar o índice espacial emMD1
. Ele nunca considera reverter os argumentos para a função e, comoSDO_RELATE
usa o índice no primeiro argumento em vez do segundo, nunca considera realmente usar o índice espacial.SDO_FILTER
se comporta da mesma maneira, tanto quanto eu posso dizer.A solução
A resposta é tão simples quanto não intuitiva. Basta mudar a ordem dos argumentos para
SDO_RELATE
:Isso faz com que ele use o índice espacial agora:
A consulta agora é quase instantânea, sendo executada em cerca de 50 milissegundos.
Ignore a
ORDERED
parte da dica nos documentos para casos como esteA documentação continua dizendo mais tarde:
A partir disso, podemos pensar que também podemos forçar o planejador a usar o índice colocando
MD2
primeiro na lista e especificando aORDERED
dica:que nos dá um plano de execução diferente:
Isso força o índice a ser usado, mas é usado de maneira errada. Ele ainda está fazendo uma verificação completa da tabela no
MD2
. Em vez de filtrarMD2
pela geometria solitária doMD1
conjunto de resultados de , ele pega quase todas as linhas da tabelaMD2
(observe a contagem de 148K na linha 2 do plano), realiza duas pesquisasMD1
(uma pela chave primária e outra comparando todas as linhas usandoMD2
o índice espacial) e bitmap e o resultado. Isso realmente piora o desempenho, fazendo uma varredura completamente desnecessária no índice espacial e trabalho extra comparando essa varredura com a chave primária.Como regra geral, você deseja colocar a tabela com o menor conjunto de geometrias (depois de outros filtros) primeiro em sua
FROM
cláusula e deixá-la usar o índice espacial na segunda tabela.Apenas especificar a
ORDERED
dica na consulta original não altera o plano do original.