1 votos

Unión espacial por tiempo en postgis

Tengo dos tablas en psql que estoy tratando de unir en base a ciertos atributos - son muy grandes - 17 M de filas y 2.7 M de filas respectivamente

mydb=> SELECT reltuples::bigint AS estimate FROM pg_class where relname='foo';
 estimate 
----------
17087196
(1 row)

mydb=> SELECT reltuples::bigint AS estimate FROM pg_class where relname='bar';
 estimate 
----------
  2763829
(1 row)

En ambos, he creado índices espaciales con

CREATE INDEX foo_gix ON foo USING GIST (the_geom);

La consulta que estoy ejecutando es un análisis de puntos en polígonos basado en datos recogidos en intervalos de tiempo históricos - contando puntos en polígonos cuando las marcas de tiempo en ambas tablas coinciden. Los puntos son ubicaciones de conexiones de teléfonos móviles ( bar_history y bar ) y los polígonos son topes alrededor de ciertas áreas ( foo_history , foo )

Se ve así:

select s.id, s.place_id, s.time, count(l.location) as total
FROM foo_history as s LEFT JOIN foo as p ON s.place_id = p.id 
LEFT JOIN bar_history l ON ST_Contains(ST_Buffer(ST_Transform(ST_SetSRID(ST_Centroid(p.polygon),
4326), 32615), 100), ST_Transform(l.location, 32615)) LEFT JOIN bar ON bar.phone_id = l.id
WHERE bar.network_id = 2
group by s.id LIMIT 5

Esta consulta devuelve los resultados con éxito en menos de unos segundos. Sin embargo, cuando añado la unión por marcas de tiempo:

select s.id, s.place_id, s.time, count(l.location) as total
FROM foo_history as s LEFT JOIN foo as p ON s.place_id = p.id 
LEFT JOIN bar_history l ON ST_Contains(ST_Buffer(ST_Transform(ST_SetSRID(ST_Centroid(p.polygon),
4326), 32615), 100), ST_Transform(l.location, 32615)) LEFT JOIN bar ON bar.phone_id = l.id
WHERE bar.network_id = 2
AND 
to_timestamp(floor((extract('epoch' from l.time::timestamp) / 600 )) * 600) = 
to_timestamp(floor((extract('epoch' from s.time::timestamp) / 600 )) * 600)
    group by s.id LIMIT 5 

la consulta se ejecuta interminablemente, a pesar de que casi todas las marcas de tiempo coincidirán en ambas tablas. No soy un experto en bases de datos.

¿Cómo puedo acelerar esta consulta o añadir algún tipo de índice que la acelere?

2voto

Patrick Puntos 20392

El problema de esta consulta es que, aunque tuviera un índice en el campo timestamp, como está utilizando funciones en la cláusula WHERE, es decir, el

to_timestamp(floor((extract('epoch' from l.time::timestamp) / 600 )) * 600)

parte, la consulta se convierte en lo que se conoce como no facturable : ver esta respuesta de stack overflow para una explicación. En resumen, no cargable, significa que cada fila tendrá que ser evaluada, por lo que su consulta está tardando una eternidad. Sargable significa argumento(s) de búsqueda, esencialmente, por lo que vale. Es un término bastante oscuro, aunque el problema es muy común.

Aquí hay un par de opciones que ayudarán con la parte de la marca de tiempo.

  1. Un índice de expresión es decir, se crea el índice en la expresión que extrae la época de la marca de tiempo, y entonces se puede escribir

    WHERE l.epoch = s.epoch

que ahora utilizará un índice.

  1. Partición de la tabla en el que se partió la tabla en base a bloques de tiempo. Es probable que esto mejore drásticamente el rendimiento si está interesado en pasos de tiempo muy pequeños.

También hay que tener en cuenta, que tu consulta espacial es muy ineficiente, ya que al utilizar ST_Buffer dentro de ST_Contains es probable que el índice espacial no se utilice. Sería mejor utilizar ST_DWithin con la distancia del buffer.

Por último, debería ejecutar EXPLAIN en todas sus consultas. Puede costar un poco acostumbrarse, pero si ves escaneos de tabla en lugar de condiciones de índice, es una pista de que algo está mal.

0voto

Anon246 Puntos 1351

He visto este post que me ha dado pistas para algo. Me explico, por favor. Yo también tengo bases de datos muy grandes, con millones de registros y no sé cómo acelerar las consultas. Quiero hacer un join espacial. Lo que he hecho es exportar una capa gpkg y creando un entorno a partir de la hierba, importarlos. A partir de ahí, con v.distance puedo hacer lo que necesito, pero los tiempos de resolución son enormes. ¿Cómo puedo hacerlo más rápido? ¿Tendría que crear índices?; ¿se puede hacer desde el propio Grass, o lo modifico en sqlite? ¿Ese índice tiene que estar en el campo de la geometría?

Muchas gracias y disculpe las molestias.

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X