Miglioramento delle prestazioni di STIntersects


11

La tabella T_PINha 300.000 pin e T_POLYGONha 36.000 poligoni. T_PINha questo indice:

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 ha:

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 query per trovare l'intersezione T_PINe T_POLYGONrichiede più di 45 minuti per eseguire:

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

Il risultato è 4.438.318 righe.

Come posso accelerare questa query?


Hai provato a usare `T_POLYGON.Coord.STIntersects (T_PIN.COORD) = 1 '?
Travis,

Sarei interessato a vedere il tuo piano di query. Lavoro su Postgres, ma devo eseguire query simili, ma su set di dati significativamente più grandi. Ho escogitato una tecnica che riduce i miei peggiori a circa 2 giorni (che sfortunatamente comporta lo scripting), ma sarei interessato a vedere prima il tuo piano di query.
John Powell,

Moltiplicando insieme i poligoni nelle mie due tabelle, ho un numero che è 7000 volte più, in termini di numero di potenziali intersezioni, rispetto a quello che hai nella tua combinazione, quindi penso che in questa luce i miei due giorni siano abbastanza buoni. Tuttavia, senza vedere un piano di query e conoscere qualcosa sul numero medio di punti per poligono, sarà difficile trovare soluzioni concrete.
John Powell,

Risposte:


7

Innanzitutto, controlla se viene utilizzato un indice spaziale osservando il piano di esecuzione della query e controlla se esiste un elemento Ricerca indice cluster (spaziale).

Supponendo che sia in uso, potresti provare ad aggiungere un filtro secondario / semplificato basato su un riquadro di delimitazione con poligoni semplificati da verificare per primo. Le corrispondenze con questi poligoni semplificati potrebbero quindi essere eseguite attraverso il filtro principale per ottenere i risultati finali.

1) Aggiungi una nuova colonna di geografia e geometria alla tabella [dbo]. [T_POLYGON]:

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

2) Crea i poligoni del riquadro (questo comporta una conversione iniziale in geometria per sfruttare 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) Crea un indice spaziale nella colonna della geografia semplificata

4) Ottieni le intersezioni rispetto a questa colonna di geografia semplificata, quindi filtra nuovamente sui tipi di dati geografici corrispondenti. All'incirca, qualcosa del genere:

;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)

EDIT : puoi sostituire (1) e (2) con questa colonna calcolata e persistente. Ringraziamo Paul White per il suggerimento.

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

Sì, è più o meno quello a cui stavo arrivando. Il problema che ho riscontrato quando "unisco" spazialmente due serie di tabelle con un'ampia area di copertura è che l'ottimizzatore va spesso per due scansioni di tabelle complete e un sacco di punti nei test poligonali.
John Powell,

2

Query come questa spesso richiedono molto tempo a causa della complessità dei poligoni. Ho visto coste complesse (ad esempio) impiegare anni per testare punti vicini ai loro confini, dover ingrandire molti livelli per scoprire se un punto si trova all'interno o all'esterno.

... così puoi provare .Reduce()i poligoni, per vedere se questo aiuta.

E per ulteriori informazioni su tale funzione, consultare http://msdn.microsoft.com/en-us/library/cc627410.aspx


1

Secondo i documenti Microsoft, gli indici spaziali verranno utilizzati con i tipi di geografia sui seguenti metodi quando compaiono all'inizio di un predicato di confronto con una WHEREclausola:

  • STIntersects
  • STDistance
  • STEquals

Solo i metodi dei tipi di geometria (elenco limitato) attiveranno l'uso dell'indice spaziale JOIN ... ON, quindi cambia il codice da utilizzare WHERE geog1.STIntersects(geog2) = 1e ciò dovrebbe migliorare la velocità.

Consiglio anche di dare consigli nella risposta di g2server e aggiungere quanto segue per filtrare e aggiungere l'indice spaziale su di esso

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

potresti quindi avere una query come la seguente (ho scritto questo post rapidamente e non ho ancora testato, questo è solo qualcosa da provare perché ho visto che la tua query e le risposte più alte pubblicate utilizzano JOIN ON spatial op = 1 che non utilizzerà un indice spaziale):

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

Cordiali saluti: Quanto sopra non funziona se SimplePolysGeogfiniscono per sovrapporsi (come in un pin può trovarsi in due geog semplificati, basta eseguirlo su persone in recinti in uno stato e poiché i polys normali condividono il confine, i riquadri di delimitazione si sovrappongono), quindi nella maggior parte degli usi casi, genererà un errore che la query secondaria ha restituito più di un risultato.

Dalla panoramica degli indici spaziali di MS Docs :

Metodi geografici supportati da indici spaziali

In determinate condizioni, gli indici spaziali supportano i seguenti metodi geografici orientati agli insiemi: STIntersects (), STEquals () e STDistance (). Per essere supportati da un indice spaziale, questi metodi devono essere utilizzati all'interno della clausola WHERE di una query e devono verificarsi all'interno di un predicato del seguente modulo generale:

geography1.method_name (geography2) comparison_operatorvalid_number

Per restituire un risultato non nullo, geography1 e geography2 devono avere lo stesso identificatore di riferimento spaziale (SRID) . Altrimenti, il metodo restituisce NULL.

Gli indici spaziali supportano i seguenti moduli predicati:


Query che utilizzano gli indici spaziali

Gli indici spaziali sono supportati solo nelle query che includono un operatore spaziale indicizzato nella clausola WHERE. Ad esempio sintassi come:

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

Query Optimizer comprende la commutatività delle operazioni spaziali (quella @a.STIntersects(@b) = @b.STInterestcs(@a)). Tuttavia, l'indice spaziale non verrà utilizzato se l'inizio di un confronto non contiene l'operatore spaziale (ad esempio WHERE 1 = spatial opnon utilizzerà l'indice spaziale). Per utilizzare l'indice spaziale, riscrivere il confronto (ad esempio WHERE spatial op = 1).

...

La seguente query funzionerà se si SimplePolysGeogssovrappongono:

;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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.