Como usar índices com junção interna no Postgis?

8

Eu tenho 2 conjuntos de pontos em 2 tabelas separadas. A tabela_a obteve 100k pontos e a tabela_b obteve 300k pontos. Estou tentando encontrar os pontos mais próximos em relação a qualquer ponto da tabela_b que esteja a 50 metros da tabela_a. Depois disso, calcule a coluna de queda, agrupe-as pela tabela_a coluna a_id e retorne o valor mais alto.

Eu escrevi uma consulta a seguir que atende essa criteira

SELECT DISTINCT ON (a_id) *
FROM (
       SELECT
         table_b.b_id,
         table_b.height - st_3ddistance(table_b.geom, table_a.geom) fall,
         table_b.geom,
         table_a.a_id
       FROM table_a
         INNER JOIN table_b ON _st_3ddwithin(table_a.geom, table_b.geom, 50)) a
WHERE fall >= 0
ORDER BY a_id, fall DESC;

Adicionei índices de geometria 3d:

CREATE INDEX table_a_geom ON table_a USING GIST (geom gist_geometry_ops_nd);
CREATE INDEX table_b_geom ON table_b USING GIST (geom gist_geometry_ops_nd);

No entanto, meu problema é que não consigo fazer a consulta para usá-los. A plaina de consulta continua escolhendo a varredura de sequência lenta. Executo alguns testes alterando _st_3ddwithin com st_3ddwithin , <<->> <50 , criando buffer de 50 me interceptando , st_3ddistance <50, mas sempre que o planejador está escolhendo a varredura de sequência. Existe uma maneira de usar índices com desempenho superior ou alterar a consulta para usar índices?

Meu plano de consulta:

Unique  (cost=10462593.70..10473018.43 rows=1 width=144)
  ->  Sort  (cost=10462593.70..10467806.06 rows=2084945 width=144)
        Sort Key: table_a.nmbayuid, ((table_b.height - st_3ddistance(table_b.geomgr, table_a.geom))) DESC
        ->  Nested Loop  (cost=0.00..10243762.28 rows=2084945 width=144)
              Join Filter: (_st_dwithin(table_a.geom, table_b.geomgr, '50'::double precision) AND ((table_b.height - st_3ddistance(table_b.geomgr, table_a.geom)) >= '0'::double precision))
              ->  Seq Scan on table_b  (cost=0.00..1459.47 rows=47147 width=96)
              ->  Materialize  (cost=0.00..10.97 rows=398 width=56)
                    ->  Seq Scan on table_a  (cost=0.00..8.98 rows=398 width=56)
Losbaltica
fonte
1
O que exatamente é e_wires_mv12404 que está no plano de consulta, mas não no SQL? Como é o plano de consulta apenas para a consulta interna? Eu sugiro não usar a função que começa com _ST. Finalmente, você poderá obter um melhor desempenho usando ST_DWithin em 2D, usando 35 metros, que é mais ou menos o mesmo que 50 metros das bordas opostas de um cubo. Como você está procurando o ponto mais próximo a 50 metros, isso pode ser um bom candidato para uma junção lateral e usar a construção ORDER BY a.geom <-> b.geom.
19418 John Powell
1
Eu tive um problema semelhante no ano passado, desenterrei este post para você , deixe-me saber se ele não responder às suas perguntas?
WxGeo
2
Se você observar a definição sql das funções, verá que as funções st_, como st_dwithin, na verdade são uma caixa delimitadora e uma chamada para a função st . É a parte da caixa delimitadora que pode usar o índice quando você chama a função st diretamente; não há como o banco de dados usar o índice. Você chama a função de verificar novamente diretamente.
Nicklas Avén
1
Você gostaria que eu a escrever-se a solução lateral se juntar, eu acho que ele iria trabalhar bem para o que você descreve
John Powell
1
As funções do @AndreSilva iniciadas por _STsão funções internas chamadas pelo PostGIS após a filtragem com um índice. Se você chamá-los diretamente, o índice não será usado.
dbaston

Respostas:

6

Primeiramente, como foi observado nos comentários, o sublinhado principal antes da função ST, ou seja, _ST_3DWithin levará o índice a não ser usado. Não consigo encontrar nenhuma menção recente a isso, mas em documentos mais antigos, se você procurar, por exemplo, _ST_Intersects, ele afirma:

Para evitar o uso do índice, use a função _ST_Intersects.

EDIT: Conforme esclarecido por @dbaston nos comentários, as funções com o sublinhado à esquerda são funções internas que não usam o índice quando chamadas e esse continua sendo o caso (embora seja difícil de encontrar nos documentos).

Sua consulta pode se beneficiar da sintaxe LATERAL JOIN, que se presta bem a problemas de k vizinho mais próximo (kNN) como este.

SELECT 
   a.a_id, 
   b.b_id
   b.height - ST_3Ddistance(b.geom, a.geom) AS fall,
  FROM table_a a
     LEFT JOIN LATERAL
       (SELECT
            b_id,         
            geom,
            height        
          FROM table_b
          WHERE ST_3Ddwithin(a.geom, geom, 50)
          AND height - ST_3Ddistance(geom, a.geom) > 0
          ORDER BY height - ST_3Ddistance(b.geom, a.geom) DESC 
          LIMIT 1
        ) b ON TRUE;

Isso permite encontrar as geometrias k mais próximas da tabela a (neste caso 1, devido ao LIMIT 1) à tabela b, ordenadas pela distância 3D entre elas. Ele é escrito usando LEFT JOIN, pois é possível que haja algumas geometrias na tabela a que não estejam a 50 metros da tabela b.

As consultas laterais permitem que você faça referência a colunas da cláusula FROM anterior, o que a torna mais poderosa do que as subconsultas padrão, consulte os documentos .

Não posso testar isso com seus dados, mas, quando executo consultas semelhantes, a instrução EXPLAIN indica o uso adequado do índice.

John Powell
fonte
Seus comentários fazem muito sentido, mas não posso aceitar a resposta porque a consulta que você forneceu é diferente da que a consulta original. Como mansão i antes "Eu não estou procurando único ponto mais próximo, mas um grupo de pontos dentro de 50 metros e, em seguida, eu estou selecionando um com maior valor de subtração (altura - ST_3Ddistance (geom, a.geom)) agrupados por A_ID
Losbaltica
Eu modifiquei sua consulta, dê uma olhada e adicionar melhorias, se necessário :)
Losbaltica
1
Modifiquei a consulta, a única coisa que faltava era "height -" na ordem em que. Agora, ele encontrará todos os pontos dentro de 50 e retornará o que tiver a maior altura - valor ST_3Ddistance (b.geom, a.geom). Não há necessidade de distinção, pois tudo isso é tratado por cada consulta lateral e LIMIT 1, ou seja, você obterá apenas o maior valor de queda para cada a_id.
John Powell
Isso agora está funcionando como o esperado originalmente? EXPLAIN parece sensato?
John Powell
Está funcionando como esperado. O desempenho da consulta é quase o mesmo, mas o custo da consulta é muito menor. Novo EXPLAIN: explan.depesz.com/s/Js5G Acho que atingi o limite de otimização de consultas e só acho que posso fazer agora é ajustar o servidor ou refatorar as tabelas / lógicas. Então isso está me respondendo a pergunta original
Losbaltica
2

Este link para a documentação do PostGIS recomenda as seguintes etapas para garantir que os índices e o planejador de consultas sejam otimizados:

  1. Verifique se as estatísticas são reunidas sobre o número e as distribuições de valores em uma tabela, para fornecer ao planejador de consultas melhores informações para tomar decisões sobre o uso do índice. ANÁLISE DE VÁCUO calculará ambos.

  2. Se a aspiração não ajudar, você pode forçar temporariamente o planejador a usar as informações do índice usando o conjunto enable_seqscan como off; comando. Dessa forma, você pode verificar se o planejador é capaz de gerar um plano de consulta acelerada por índice para sua consulta. Você só deve usar este comando apenas para depuração: de um modo geral, o planejador sabe melhor do que você sobre quando usar índices. Depois de executar sua consulta, não se esqueça de ativar ENABLE_SEQSCAN novamente, para que outras consultas utilizem o planejador normalmente.

  3. Se definir enable_seqscan como off; ajuda a executar sua consulta, é provável que seu Postgres provavelmente não esteja ajustado para o seu hardware. Se você achar que o planejador está errado quanto ao custo das varreduras seqüenciais versus indexadas, tente reduzir o valor de random_page_cost no postgresql.conf ou use set random_page_cost como 1.1 ;. O valor padrão para o parâmetro é 4, tente configurá-lo para 1 (no SSD) ou 2 (em discos magnéticos rápidos). Diminuir o valor torna o planejador mais inclinado a usar as verificações de índice.

  4. Se definir enable_seqscan como off; não ajuda sua consulta, pode acontecer que você use uma construção que o Postgres ainda não consiga desembaraçar. Uma subconsulta com seleção embutida é um exemplo - você precisa reescrevê-la para que o planejador de formulários possa otimizar, por exemplo, uma JUNTA LATERAL.

Portanto, primeiro tente as etapas 1 a 3 antes de reescrever sua consulta para usar os índices. Se isso não funcionar, você pode tentar modificar a consulta.

Acredito (o melhor de minha capacidade de ativar o SQL sem executar o código) que a consulta abaixo retornará resultados idênticos aos seus, mas não sei se será mais eficiente.

SELECT DISTINCT on (a_id),
    table_b.b_id as b_id,
    table_b.height - st_3ddistance(table_b.geom, table_a.geom) as fall,
    table_b.geom as b_geom,
    table_a.a_id as a_id
    FROM table_a
         INNER JOIN table_b ON _st_3ddwithin(table_a.geom, table_b.geom, 50)) a
WHERE fall >= 0
ORDER BY a_id, fall DESC;
jgm_GIS
fonte
Muito interessante, depois de alterar _st_3ddwithin para st_dwithin, como outros comentários sugeridos e executar VACUUM ANALYZE depois, o planejador finalmente começa a pegar o índice!
Losbaltica
0

Se você estiver usando o Postgres 10 (ou mais recente), recomendo fortemente que você carregue seus dados em tabelas paralelas.

Você provavelmente precisará gastar tempo ajustando-o (particionamento de dados e número de trabalhadores), mas acho que vale a pena. Teoricamente, o KNN é altamente paralelelizável, alcançando complexidades de tempo constantes, até O (1) se a quantidade de trabalhadores for igual ao número de elementos em que uma operação do KNN será calculada.

Algumas referências práticas sobre o carregamento dos dados e a execução das consultas são fornecidas aqui . Ele fornece alguns detalhes sobre a execução do plano (para forçar mais trabalhadores a serem acionados) aqui . É importante observar que os scripts paralelos envolvem muita coordenação de tarefas, para que o limite teórico extremo de fornecer a paralelização mais extrema não seja válido na prática, devido a redes e outras características de design de sistemas.

Ricardo Barros Lourenço
fonte