Mejora del rendimiento de las infecciones de transmisión sexual


11

La mesa T_PINtiene 300,000 pines y T_POLYGON36,000 polígonos. T_PINtiene este índice:

CREATE SPATIAL INDEX [T_PIN_COORD] ON [dbo].[T_PIN]
(
[Coord]
)USING  GEOGRAPHY_GRID 
WITH (GRIDS =(LEVEL_1 = HIGH,LEVEL_2 = HIGH,LEVEL_3 = HIGH,LEVEL_4 = HIGH), 
CELLS_PER_OBJECT = 128, PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, 
SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, 
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON [PRIMARY];

T_POLYGON tiene:

CREATE SPATIAL INDEX [T_POLYGON_COORD] ON [dbo].[T_POLYGON]
(
[COORD]
)USING  GEOGRAPHY_GRID 
WITH (GRIDS =(LEVEL_1 = HIGH,LEVEL_2 = HIGH,LEVEL_3 = HIGH,LEVEL_4 = HIGH), 
CELLS_PER_OBJECT = 128, PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, 
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) 
ON [PRIMARY];

Una consulta para encontrar la intersección T_PINy T_POLYGONtarda más de 45 minutos en ejecutarse:

SELECT COUNT(*)
FROM T_PIN 
INNER JOIN T_POLYGON
    ON T_PIN.Coord.STIntersects(T_POLYGON.COORD) = 1;

El resultado es 4.438.318 filas.

¿Cómo puedo acelerar esta consulta?


¿Has intentado usar `T_POLYGON.Coord.STIntersects (T_PIN.COORD) = 1 '?
travis

Me interesaría ver su plan de consultas. Trabajo en Postgres, pero tengo que ejecutar consultas similares, pero en conjuntos de datos significativamente más grandes. Se me ocurrió una técnica que reduce mis peores a aproximadamente 2 días (lo que desafortunadamente implica secuencias de comandos), pero me interesaría ver primero su plan de consulta.
John Powell

Multiplicando los polígonos en mis dos tablas juntas, tengo un número que es 7000 veces más, en términos de la cantidad de intersecciones potenciales, que el que tiene en su combinación, por lo que creo que mis dos días se ven bastante bien. Sin embargo, sin ver un plan de consulta y saber algo sobre el número promedio de puntos por polígono, será difícil encontrar soluciones concretas.
John Powell

Respuestas:


7

Primero, verifique si se está utilizando un índice espacial mirando el plan de ejecución de la consulta y vea si hay un elemento de Búsqueda de índice agrupado (espacial).

Suponiendo que se esté utilizando, puede intentar agregar un filtro secundario / simplificado basado en un cuadro delimitador con polígonos simplificados para verificar primero. Las coincidencias con estos polígonos simplificados podrían ejecutarse a través del filtro primario para obtener los resultados finales.

1) Agregue una nueva columna de geografía y geometría a la tabla [dbo]. [T_POLYGON]:

ALTER TABLE [dbo].[T_POLYGON] ADD SimplePolysGeom geometry;
ALTER TABLE [dbo].[T_POLYGON] ADD SimplePolysGeog geography;

2) Cree los polígonos del cuadro delimitador (esto implica una conversión inicial a geometría para aprovechar STEnvelope ()):

UPDATE [dbo].[T_POLYGON] SET SimplePolysGeom = geometry::STGeomFromWKB(
    COORD.STAsBinary(), COORD.STSrid).STEnvelope();

UPDATE [dbo].[T_POLYGON] SET SimplePolysGeog = geography::STGeomFromWKB(
    SimplePolysGeom.STAsBinary(), SimplePolysGeom.STSrid);

3) Crear un índice espacial en la columna de geografía simplificada

4) Obtenga las intersecciones contra esta columna de geografía simplificada, luego filtre nuevamente en los tipos de datos de geografía coincidentes. Aproximadamente, algo como esto:

;WITH cte AS
(
   SELECT pinID, polygonID FROM T_PIN INNER JOIN T_POLYGON
    ON T_PIN.Coord.STIntersects(T_POLYGON.SimplePolysGeog ) = 1
)
SELECT COUNT(*)
FROM T_PIN 
INNER JOIN T_POLYGON
    ON T_PIN.Coord.STIntersects(T_POLYGON.COORD) = 1
    AND T_PIN.pinID IN (SELECT pinID FROM cte)
    AND T_POLYGON.polygonID IN (SELECT polygonID FROM cte)

EDITAR : puede reemplazar (1) y (2) con esta columna calculada y persistente. crédito a Paul White por la sugerencia.

ALTER TABLE [dbo].[T_POLYGON] ADD SimplePolysGeog AS  ([geography]::STGeomFromWKB([geometry]::STGeomFromWKB([COORD].[STAsBinary](),[COORD].[STSrid]).STEnvelope().STAsBinary(),(4326))) PERSISTED

Sí, eso es más o menos a lo que me refería. El problema que encontré al "unir" espacialmente dos conjuntos de tablas con un área de cobertura amplia es que el optimizador a menudo utiliza dos escaneos completos de tablas y cargas de puntos en las pruebas de polígonos.
John Powell

2

Las consultas como esta suelen llevar mucho tiempo debido a la complejidad de los polígonos. He visto costas complejas (por ejemplo) que tardan años en probar puntos que están cerca de sus límites, teniendo que hacer zoom en muchos niveles para encontrar si un punto está dentro o fuera.

... para que puedas probar .Reduce()los polígonos, para ver si eso ayuda.

Y para obtener más información sobre esa función, consulte http://msdn.microsoft.com/en-us/library/cc627410.aspx


1

Según los documentos de Microsoft, los índices espaciales se usarán con los tipos de geografía en los siguientes métodos cuando aparecen al comienzo de un predicado de comparación con una WHEREcláusula:

  • STIntersects
  • STDistance
  • STEquals

Solo los métodos de tipos de geometría (lista restringida) activarán el uso del índice espacial JOIN ... ON, así que cambie su código para usarlo WHERE geog1.STIntersects(geog2) = 1y eso debería mejorar la velocidad.

También recomiendo tomar consejos en la respuesta de g2server y agregar lo siguiente para filtrar y agregar índice espacial en él

ALTER TABLE [dbo].[T_POLYGON] ADD SimplePolysGeog AS
     ([geography]::STGeomFromWKB([geometry]::STGeomFromWKB([COORD].[STAsBinary](),
                                                           [COORD].[STSrid])
                 .STEnvelope().STAsBinary(),(4326))) PERSISTED

entonces podría tener una consulta como la siguiente (escribí esta publicación rápidamente y aún no la he probado, esto es solo algo para probar porque vi que su consulta y las respuestas más altas publicadas usan JOIN ON espacial op = 1 que no usará un índice espacial):

SELECT   
     (SELECT p2.polygon_id
      FROM   T_Polygon p2
      WHERE  p2.coords.STIntersects(t.coords) = 1),
     t.pin_id
FROM     T_PIN t
WHERE    
     (SELECT t.coords.STIntersects(p.coords)
      FROM   T_POLYGON p
      WHERE  t.coords.STIntersects(p.SimplePolysGeog) = 1) = 1

FYI: Lo anterior no funciona si SimplePolysGeogtermina superpuesto (como en un pin puede estar en dos geogs simplificados, solo ejecuté esto en personas en recintos en un estado y dado que los polys normales comparten límites, los cuadros delimitadores se superponen), por lo que en la mayoría de los usos casos, arrojará un error que la subconsulta arrojó más de un resultado.

Del resumen de índices espaciales de MS Docs :

Métodos de geografía soportados por índices espaciales

Bajo ciertas condiciones, los índices espaciales admiten los siguientes métodos de geografía orientados a conjuntos: STIntersects (), STEquals () y STDistance (). Para ser soportados por un índice espacial, estos métodos deben usarse dentro de la cláusula WHERE de una consulta, y deben ocurrir dentro de un predicado de la siguiente forma general:

geography1.method_name (geography2) compare_operatorvalid_number

Para devolver un resultado no nulo, geography1 y geography2 deben tener el mismo Identificador de referencia espacial (SRID) . De lo contrario, el método devuelve NULL.

Los índices espaciales admiten las siguientes formas de predicado:


Consultas que usan índices espaciales

Los índices espaciales solo se admiten en consultas que incluyen un operador espacial indexado en la cláusula WHERE. Por ejemplo, sintaxis como:

[spatial object].SpatialMethod([reference spatial object]) [ = | < ] [const literal or variable]

El optimizador de consultas comprende la conmutatividad de las operaciones espaciales (eso @a.STIntersects(@b) = @b.STInterestcs(@a)). Sin embargo, el índice espacial no se usará si el comienzo de una comparación no contiene el operador espacial (por ejemplo WHERE 1 = spatial op, no usará el índice espacial). Para usar el índice espacial, reescriba la comparación (por ejemplo WHERE spatial op = 1).

...

La siguiente consulta funcionará si se SimplePolysGeogssuperpone:

;WITH cte AS
(
   SELECT T_PIN.PIN_ID, 
          T_POLYGON.POLYGON_ID, 
          T_POLYGON.COORD 
   FROM T_PIN 
   INNER JOIN T_POLYGON
   ON T_PIN.COORD.STIntersects(T_POLYGON.SimplePolysGeog) = 1
)

SELECT COUNT(*)
FROM T_PIN 
INNER JOIN cte
ON T_PIN_PIN_ID = cte.PIN_ID
where cte.[COORD].STIntersects(T_PIN.COORD) = 1
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.