web-dev-qa-db-fra.com

Améliorer la performance des STITRESSECTS

Tableau T_PIN a 300 000 épingles et T_POLYGON a 36 000 polygones. T_PIN a cet index:

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 possède:

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_PIN et T_POLYGON prend plus de 45 minutes pour 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?

11
seb49

Tout d'abord, vérifiez si un indice spatial est utilisé en examinant le plan d'exécution de la requête et de voir s'il existe un élément de recherche d'index en cluster (spatial).

En supposant qu'il soit utilisé, vous pouvez essayer d'ajouter un filtre secondaire/simplifié basé sur une boîte de sélection avec des polygones simplifiés pour vérifier en premier. Les matchs contre ces polygones simplifiés pourraient ensuite être exécutés à 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] Table:

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 de sélection (cela implique une conversion initiale en géométrie pour profiter de STENVENVOPE ()):

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 indice spatial sur la colonne de géographie simplifiée

4) Obtenez les intersections contre cette colonne de géographie simplifiée, puis filtrez à nouveau sur les types de données de géographie correspondants. Grossièrement, 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)

ÉDITER : Vous pouvez remplacer (1) et (2) avec cette colonne calculée et persistante. Crédit à Paul Blanc pour la suggestion.

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

Les questions comme celle-ci prennent souvent beaucoup de temps à cause de la complexité des polygones. J'ai vu des côtes complexes (par exemple) prendre des âges pour tester des points à proximité de leurs frontières, ayant à zoomer de nombreux niveaux pour déterminer si un point est à l'intérieur ou à l'extérieur.

... Donc, vous pouvez essayer .Reduce() 'ing les polygones, pour voir si cela aide.

Et pour plus de cette fonction, regardez http://msdn.microsoft.com/en-us/library/cc627410.aspx

2
Rob Farley

Selon Microsoft Docs, les index spatiaux seront utilisés avec des types de géographie sur les méthodes suivantes lorsqu'ils apparaissent au début d'un prédicat de comparaison avec une clause WHERE:

  • STIntersects
  • STDistance
  • STEquals

Seuls les types de types de géométrie (liste restreinte) déclenchera l'utilisation de l'index spatial dans JOIN ... ON, Modifiez ainsi votre code pour utiliser WHERE geog1.STIntersects(geog2) = 1 et cela devrait améliorer la vitesse.

Je recommande également de prendre des conseils Réponse de G2Server et d'ajouter ce qui suit pour filtrer et ajouter un index spatial sur celui-ci

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

vous pouvez alors avoir une requête - comme Ce qui suit (j'ai écrit ce message rapidement et n'a pas encore testé, il s'agit simplement de quelque chose à essayer parce que j'ai vu que votre requête et les réponses postées les plus élevées utilisent spatial op = 1 qui n'utilisera pas un 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

FYI: ce qui précède ne fonctionne pas si SimplePolysGeog fin de se chevaucher (comme dans une goupille peut être dans deux gènes simplifiées, il suffit de courir sur des personnes en précision dans un état et, étant donné que les polys normaux partagent la limite, les boîtes à bornes se chevauchent ), donc dans la plupart des cas d'utilisation, il lancera une erreur que la sous-requête est renvoyée à plus d'un résultat.

De MS DOCS ' Aperçu des indices spatiaux :

Méthodes de géographie supportées par des index spatiaux

Dans certaines conditions, les indices spatiaux appuient les méthodes de géographie orientées suivantes: STITRESSECTS (), Stiques () et STDISTANCE (). Pour être soutenu par un indice spatial, ces méthodes doivent être utilisées dans la clause d'une requête, et elles doivent se produire dans un prédicat de la forme générale suivante:

Geography1.Method_Name (géography2) Comparaison_OperatorValid_Number

Pour renvoyer un résultat non nul, Geography1 et Geography2 DOIT avoir le même identifiant de référence spatial (srid) . Sinon, la méthode renvoie NULL.

Les index spatiaux prennent en charge les formulaires de prédicats suivants:


Questions 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é de 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 (que @a.STIntersects(@b) = @b.STInterestcs(@a)). Toutefois, l'indice spatial ne sera pas utilisé si le début d'une comparaison ne contient pas l'opérateur spatial (par exemple WHERE 1 = spatial op N'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 si SimplePolysGeogs chevauchement:

;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
1
pbordeaux