Como corrigir problema de desempenho no PostGIS ST_Intersects?

9

Sou iniciante no postgis e tenho um problema no desempenho da consulta.

Esta minha consulta:

SELECT DISTINCT ON (userid) userid ,ST_AsText(position), timestamp  
FROM table1 
WHERE ST_Intersects ( ST_GeomFromText('a multiypolygon geom goes here',4326),position) 
ORDER BY userid, timestamp desc

e o problema é que meu multipolígono inclui polígonos MUITO grandes (600 páginas de comprimento no word doc!) e demorou mais de duas horas para executar!

Existe uma maneira de otimizar minha consulta ou usar outra maneira?

Por favor, sua ajuda é muito apreciada!

Sara
fonte

Respostas:

8

O que você deve fazer é colocar seu grande multipolígono em uma tabela como polígonos únicos (com ST_Dump) e colocar um índice nela. Algo como:

CREATE TABLE big_polygon as
SELECT (ST_Dump( ST_GeomFromText('a multiypolygon geom goes here',4326))).geom as geom;

-- It is always great to put a primary key on the table
ALTER table big_polygon ADD Column gid serial PRIMARY KEY;

-- Create the index
CREATE INDEX idx_big_polygon_geom
on big_polygon
USING gist(geom);

-- To give the database some information about how the index looks
analyze big_polygon;

-- Then you go:
SELECT DISTINCT ON (userid) userid ,ST_AsText(position), timestamp  
FROM table1, big polygon WHERE ST_Intersects ( big_polygon.geom,position) 
ORDER BY userid, timestamp desc;

Isso deve ser muito mais rápido por vários motivos.

Nicklas Avén
fonte
Obrigado Nicklas por esta ótima resposta. Desculpe, esqueci de mencionar que tenho mais de um polígono e eles já estão armazenados em uma tabela com índice. Mas pensei que fornecer os dados geom diretamente seria mais rápido. No entanto, eu tento da maneira que você sugere, mas ainda leva muito tempo! alguma outra sugestão?
Sara
@Sara. Ok, então você tentou dividir os multigeoemtries em geometrias únicas, como eu sugiro com ST_Dump?
Nicklas Avén 6/02/2012
De quantas posições de usuário estamos falando? Quantos polígonos grandes? O que você obtém dos SELECT ST_npoints (geom) de big_polygons_table ;?
Nicklas Avén 06/02/2012
Sinto muito, deixe-me explicar mais sobre minhas tabelas para tornar mais claro para você: Eu tenho table1, que inclui uma coluna geom que tem cerca de 230 linhas e em cada linha há um multipolígono (eles representam países e variam em tamanho) e tenha índice na the_geom col. Tabela2, que inclui a coluna da posição (pontos), carimbo de data e hora, ID do usuário e ID (pk) e 3 índices criados usando (posição, carimbo de data e hora, ID do usuário) .Esta tabela é muito grande, com cerca de 103496003 linhas. O número máximo de ST_npoints é 1440430 e o número mínimo é 16. Me desculpe se eu fiz você confundir, mas eu realmente preciso da sua ajuda! Obrigado
Sara
2

Depende do tipo de qualidade - precisão que você precisa. Obviamente, você pode simplificar os polígonos usando: http://postgis.net/docs/ST_Simplify.html

O que fiz muitas vezes durante o desenvolvimento do meu aplicativo GIS foi pensar sobre a melhor maneira de minimizar os dados. Por exemplo. pré-selecione os polígonos dentro da caixa de limite, por exemplo. - Dependendo do nível de zoom, você não precisa de resultados ultra precisos (st_simplify), etc.

Espero que tenha ajudado um pouco!

Styp
fonte
Obrigado Martin pela sua resposta rápida. Meu problema é que preciso que o resultado fosse muito preciso, então acho que essa função não vai me ajudar aqui! mas Obrigado pela sugestão
Sara
0

Dependendo do seu conhecimento em postgres e / ou sql, você tem várias opções:

  1. analise a consulta através do comando EXPLAIN para descobrir se você está atingindo um gargalo específico. Aviso: às vezes, a saída de EXPLAIN pode ser difícil de entender

  2. se você espera que a maior parte ou uma parte significativa das geometrias da tabela1 NÃO cruze o multipolígono, tente aplicar uma condição preliminar a um polígono mais simples (ou seja, quebrando o multigráfico em pedaços menores) e execute a interseção multipolígena mais pesada apenas em esses resultados. Veja abaixo um exemplo.

  3. se e somente se CPU é o gargalo (ou seja, o servidor é cruzamentos de computação preso) I devidamente sugiro que você obter um maior, mais rápido CPU, mais poderoso ou alugar um one-time de alta CPU Instância off EC2 da Amazon e destruí-lo quando você estiver feito

Exemplo de consulta para o item 2:

SELECT DISTINCT ON (st1.userid) st1.userid ,ST_AsText(st1.position), st1.timestamp  
FROM (
    select userid, position, timestamp from table1 
    WHERE ST_Intersects ( YOUR_MULTIPOL_BOUNDS_HERE,position)
) as st1 
WHERE ST_Intersects ( ST_GeomFromText('a multiypolygon geom goes     here',4326),st1.position) 
ORDER BY st1.userid, st1.timestamp desc

Para melhorar o desempenho, você também pode materializar temporariamente a subseleção st1 como uma tabela para poder indexá-la.

@Nicklas tem razão em apontar nos comentários que o exemplo da sugestão 2 não deve ajudar. Ele está certo, mas acho que estou (parcialmente) certo também.

De fato, parece que uma pergunta muito semelhante foi feita (e respondida) apenas em novembro passado no postgis ML:

http://postgis.refractions.net/pipermail/postgis-users/2011-November/031344.html

e acontece que a sugestão é realmente quebrar o polígono para que o índice possa filtrar com mais eficiência as interseções falsas que seriam acionadas por uma simples verificação de limite.

unicoletti
fonte
a sugestão 2 não deve ajudar, porque é exatamente isso que o índice está fazendo. Portanto, essa construção estará fazendo o mesmo novamente.
Nicklas Avén 6/02/2012
@ NicklasAvén você está certo, eu alterado a resposta
unicoletti
0

Usando ST_SubDivide()

Para a versão 2.2 do Postgis, você pode usar ST_SubDivide.

ST_Subdivide - Retorna um conjunto de geometria em que nenhuma geometria no conjunto tem mais do que o número especificado de vértices.

setof geometry ST_Subdivide(geometry geom, integer max_vertices=256);

Você também pode

  • use uma tabela temporária
  • Um índice

Aqui, usamos ST_SubDividepara dividir o polígono em subpolígonos com 10 ou menos vértices.

CREATE TEMP TABLE divided AS
SELECT ST_SubDivide(bigmultipolygon,10)::geometery AS t(geom);

CREATE INDEX divided_idx ON divided USING gist(geom);

Então

SELECT DISTINCT ON (userid) userid ,ST_AsText(position), timestamp  
FROM table1
JOIN divided AS d
  ON ST_Intersects( d.geom, position )
ORDER BY userid, timestamp desc;

Não faça o acima, ele introduz erros de arredondamento

Afinação geral

Veja também a seção intitulada Dicas de desempenho nos documentos. Verifique se você está sintonizado adequadamente. Considere aumentar max_parallel_workers_per_gatherpara aproveitar a paralelização (atualmente o padrão é desativado).

Evan Carroll
fonte