tl;dr: Pruebe la consulta en Sin embargo, primero. Esto, aunque sea, sólo tiene sentido si la geometría de la hoja no necesita disolverse y ST_Union
puede sustituirse por ST_Collect
.
Siguiendo mis comentarios:
A lo que me refería es a "externalizar" el sindicato en un CTE es decir, un WITH
cláusula, así:
WITH
blade AS (
SELECT ST_Collect(wkb_geometry) AS geom
FROM b
WHERE group_id = 43 and type_id = 12
)
SELECT a.someid,
ST_Difference(a.wkb_geometry, b.geom) AS geom
FROM a
JOIN blade AS b
ON ST_Intersects(a.wkb_geometry, b.geom);
Un CTE se ejecutará una vez por cada transacción, y puede ser visto como una tabla temporal eliminada en el commit; esto ayuda a evitar la creación de conjuntos de filas una y otra vez, y se ve mejor.
Utilicé ST_Collect
en lugar de ST_Union
en la mayoría de los casos, esto debería devolver el mismo MULTI
geometría a la vez que son órdenes de magnitud más rápidas.
También utilicé un (INNER) JOIN
en lugar de un WHERE
filtro; esto es sólo para la legibilidad , en la mayoría de los casos .
Obsérvese también que no hay (posiblemente costosos) GROUP BY
operaciones.
Sin embargo:
El principal motor del rendimiento, y en realidad el que hace su trabajo, es el Planificador de consultas La Planificador optimizará al máximo su consulta aplanando, sustituyendo, moviendo e incluso no ejecutando partes de la misma. Y en la mayoría de los casos es correcto.
Su eficacia está ligada principalmente a la actualización de las estadísticas de la tabla; se puede (y se debe, por ejemplo, después de crear índices o UPDATE/INSERT/DELETE
operaciones) actualizarlas manualmente ejecutando VACUUM ANALYZE/FULL
en una o todas las mesas.
Ahora, el punto clave; hay consultas que el planificador no puede optimizar, por diseño o por lógica, y Los CTEs son uno de esos (las funciones son otra, por lo que, por ejemplo, la unión en su función de diferencia no puede optimizarse aunque sea posible).
En su caso, la unión dentro de una subconsulta real puede ser por tanto beneficiosa (y la mejor opción):
SELECT a.someid,
ST_Difference(a.wkb_geometry, b.geom) AS geom
FROM a
JOIN (
SELECT ST_Collect(wkb_geometry) AS geom
FROM b
WHERE group_id = 43 and type_id = 12
) AS b
ON ST_Intersects(a.wkb_geometry, b.geom);
Notas sobre el índice y el rendimiento:
En general, la ejecución de consultas de relaciones geométricas y/o de manipulación en grandes y/o MULTI
geometrías de la red se comportan peor que en muchas geometrías individuales pequeñas; para cada conjunto de puntos de entrada, cada vértice de la geometría grande tiene que ser atravesado y comprobado.
Dado que un índice espacial funciona con bbox árboles, el filtrado de la (no) relación es rapidísimo para geometrías pequeñas simplemente comparando sólo cuatro vértices en una rama del árbol, y sólo hacer la prueba de intersección geométrica en unos pocos elegidos; teniendo una bbox sin embargo, lo más probable es que incluya muchas o todas las demás comparadas, incluso si las propias geometrías no se cruzan geográficamente. En ese caso, PostGIS tendrá que hacer el costoso filtro de intersección en demasiadas geometrías, lo que anula el propósito de un índice.
Dicho esto, ST_Difference
es complicado, ya que lo mejor es que la hoja sea una geometría unificada (para evitar el corte de productos cruzados de la misma geometría, dando lugar a duplicados). Una unión de geometrías indexadas hará que la gemetría combinada no sea SARGable Es decir, no puede tener o utilizar un índice.
Pero es posible que, cuando se trabaje con un subconjunto en el que se garantice que cada geometría se cruza con la hoja, una unión de las mismas funcione igual o incluso más rápido, ya que no hay una búsqueda de índice (en este caso innecesaria) primero, y al tener esa segunda gran MULTI
geometría entonces en la memoria ya.
Teniendo en cuenta esto, corriendo:
SELECT ST_Difference(a.geom, b.geom) AS geom
FROM (
SELECT ST_Collect(wkb_geometry) AS geom
FROM a
WHERE <column_filter_condition>
) AS a
CROSS JOIN (
SELECT ST_Collect(wkb_geometry) AS geom
FROM b
WHERE group_id = 43 and type_id = 12
) AS b;
podría ser incluso más rápido, si puede filtrar a
por adelantado (sin comprobar la intersección) en un subconjunto superpuesto.
Esto se fue rápidamente de las manos en el ámbito, no estoy seguro de que esto ayude. Pero ya que preguntaste...
¿Podría responder si alguno de los anteriores hizo ...o no ha funcionado?
1 votos
¿qué ocurre cuando se añade una cláusula "GROUP BY"? ¿Intentó utilizar SELECT DISTINCT?
0 votos
Sólo intenté agrupar por geometría, pero cuando también agrupé por id funcionó
0 votos
En realidad yo diría que la unión ambos entradas (¿con una agrupación basada en id?); al hacerlo, se recrea la unión de b para cada fila de a. además, un índice no ayuda mucho aquí de todos modos.
0 votos
@ThingumaBob ¿Cómo puedo modificar para que la unión b sea sólo una vez?
0 votos
Mover la unión con filtro a un CTE, al menos para la tabla 'blade' y ver si algún índice hace ayuda con geometrías más pequeñas en la otra tabla. Supongo que, si la mayoría o todas las geometrías en el subconjunto de esa otra tabla se cortará un índice es bastante inútil, y así por separado la unión de ambos con los filtros necesarios sería aún más simple. manejo de atributos sería peor de esa manera, sin embargo.
0 votos
@ThingumaBob No entiendo como hacer eso, soy un novato en postgis. ¿Conoces algún ejemplo que muestre cómo hacerlo? El código se ejecuta en unos 40 s, con 400 000 geometrías en a (8000 de ellas intersectan b), y 300 en b. No sé si esto es lento o rápido.