Amélioration des performances de STIntersects

11

La table T_PINa 300 000 broches et T_POLYGON36 000 polygones. T_PINa cet 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 a:

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

Une requête pour trouver l'intersection de T_PINet T_POLYGONprend plus de 45 minutes à exécuter:

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

Le résultat est de 4 438 318 lignes.

Comment puis-je accélérer cette requête?

seb49
la source
Avez-vous essayé d'utiliser `T_POLYGON.Coord.STIntersects (T_PIN.COORD) = 1 '?
travis
Je serais intéressé de voir votre plan de requête. Je travaille sur Postgres, mais je dois exécuter des requêtes similaires, mais sur des ensembles de données beaucoup plus volumineux. J'ai trouvé une technique qui réduit mes pires à environ 2 jours (ce qui implique malheureusement des scripts), mais je serais intéressé de voir votre plan de requête en premier.
John Powell
En multipliant les polygones dans mes deux tables ensemble, j'ai un nombre qui est 7000 fois plus, en termes de nombre d'intersections potentielles, que vous n'en avez dans votre combinaison, donc je pense que dans cette lumière mes deux jours semblent assez bons. Cependant, sans voir un plan de requête et connaître quelque chose sur le nombre moyen de points par polygone, il sera difficile de trouver des solutions concrètes.
John Powell

Réponses:

7

Tout d'abord, vérifiez si un index spatial est utilisé en examinant le plan d'exécution des requêtes et voyez s'il existe un élément de recherche d'index clusterisé (spatial).

En supposant qu'il soit utilisé, vous pouvez essayer d'ajouter un filtre secondaire / simplifié basé sur une boîte englobante avec des polygones simplifiés à vérifier en premier. Des correspondances avec ces polygones simplifiés pourraient ensuite être exécutées à travers le filtre principal pour obtenir les résultats finaux.

1) Ajoutez une nouvelle colonne de géographie et de géométrie à la table [dbo]. [T_POLYGON]:

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

2) Créez les polygones de la boîte englobante (cela implique une conversion initiale en géométrie pour tirer parti de 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) Créer un index spatial sur la colonne de géographie simplifiée

4) Obtenez les intersections avec cette colonne géographique simplifiée, puis filtrez à nouveau sur les types de données géographiques correspondants. En gros, quelque chose comme ça:

;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 : vous pouvez remplacer (1) et (2) par cette colonne persistante calculée. crédit à Paul White pour la suggestion.

ALTER TABLE [dbo].[T_POLYGON] ADD SimplePolysGeog AS  ([geography]::STGeomFromWKB([geometry]::STGeomFromWKB([COORD].[STAsBinary](),[COORD].[STSrid]).STEnvelope().STAsBinary(),(4326))) PERSISTED
g2server
la source
Ouais, c'est plus ou moins ce que je voulais dire. Le problème que j'ai constaté lors de la "jonction" spatiale de deux ensembles de tables avec une large zone de couverture est que l'optimiseur va souvent pour deux analyses de table complètes et des charges de points dans les tests de polygones.
John Powell
2

De telles requêtes prennent souvent beaucoup de temps en raison de la complexité des polygones. J'ai vu des côtes complexes (par exemple) prendre du temps pour tester des points qui sont près de leurs limites, devant zoomer sur plusieurs niveaux pour savoir si un point est à l'intérieur ou à l'extérieur.

... pour que vous puissiez essayer .Reduce()les polygones, pour voir si cela aide.

Et pour en savoir plus sur cette fonction, consultez http://msdn.microsoft.com/en-us/library/cc627410.aspx

Rob Farley
la source
1

Selon les documents Microsoft, les index spatiaux seront utilisés avec les types géographiques sur les méthodes suivantes lorsqu'ils apparaissent au début d'un prédicat de comparaison avec une WHEREclause:

  • STIntersects
  • STDistance
  • STEquals

Seules les méthodes des types de géométrie (liste restreinte) déclencheront l'utilisation de l'index spatial dans JOIN ... ON, donc changez votre code à utiliser WHERE geog1.STIntersects(geog2) = 1et cela devrait améliorer la vitesse.

Je recommande également de prendre des conseils dans la réponse de g2server et d'ajouter ce qui suit pour le filtrage et ajouter un index spatial dessus

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

vous pourriez alors avoir une requête comme la suivante (j'ai écrit ce post rapidement et je n'ai pas encore testé, c'est juste quelque chose à essayer car j'ai vu que votre requête et les réponses les plus élevées utilisent JOIN ON spatial op = 1 qui n'utilisera pas de indice spatial):

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

Pour info: ce qui précède ne fonctionne pas si SimplePolysGeogfinissent par se chevaucher (comme dans une épingle peut être dans deux geogs simplifiés, il suffit de l'exécuter sur les personnes dans les circonscriptions dans un état et puisque les polys normaux partagent la frontière, les boîtes de délimitation se chevauchent), donc dans la plupart des utilisations cas, il générera une erreur indiquant que la sous-requête a renvoyé plusieurs résultats.

De la vue d'ensemble des index spatiaux de MS Docs :

Méthodes de géographie prises en charge par les index spatiaux

Dans certaines conditions, les index spatiaux prennent en charge les méthodes de géographie orientées ensemble suivantes: STIntersects (), STEquals () et STDistance (). Pour être prises en charge par un index spatial, ces méthodes doivent être utilisées dans la clause WHERE d'une requête et elles doivent se produire dans un prédicat de la forme générale suivante:

geography1.method_name (geography2) comparaison_operatorvalid_number

Pour renvoyer un résultat non nul, geography1 et geography2 doivent avoir le même identifiant de référence spatiale (SRID) . Sinon, la méthode renvoie NULL.

Les index spatiaux prennent en charge les formes de prédicat suivantes:


Requêtes qui utilisent des index spatiaux

Les index spatiaux ne sont pris en charge que dans les requêtes qui incluent un opérateur spatial indexé dans la clause WHERE. Par exemple, la syntaxe telle que:

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

L'optimiseur de requêtes comprend la commutativité des opérations spatiales (cela @a.STIntersects(@b) = @b.STInterestcs(@a)). Cependant, l'index spatial ne sera pas utilisé si le début d'une comparaison ne contient pas l'opérateur spatial (par exemple, WHERE 1 = spatial opn'utilisera pas l'index spatial). Pour utiliser l'index spatial, réécrivez la comparaison (par exemple WHERE spatial op = 1).

...

La requête suivante fonctionnera en cas de SimplePolysGeogschevauchement:

;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
pbordeaux
la source