web-dev-qa-db-fra.com

Comment optimiser l'exécution de la stdistance?

Je crée une table temporaire lors de l'exécution de la procédure stockée avec la structure suivante:

[ID] BIGINT
[Point] GEOGRAPHY

le ID n'est pas unique - il y a environ 200 enregistrements pour chaque ID.

J'ai besoin de trouver une liste avec une liste distincte IDs pour laquelle il existe au moins un Point à Point distance plus grande que la valeur constante (par exemple 200 Mètres) .

Donc, j'utilise quelque chose comme ça:

SELECT DISTINCT DS1.[ID]
FROM DataSource DS1
INNER JOIN DataSource DS2
    ON DS1.[ID] = DS2.[ID]
WHERE DS1.Point.STDistance(DS2.Point) > 200

Pour 23 000 points, la requête est exécutée pour 4-5 Secondes. Comme je m'attends à avoir plus de valeurs, j'ai besoin de trouver une meilleure solution.

Je suppose que s'il y a un moyen plus rapide, je peux toujours créer une table matérialisée et mettre en œuvre une logique supplémentaire qui le calculera sur ID base.

J'ai créé un indice spatial, mais l'optimiseur de requêtes ne l'utilise pas. Si j'utilise un hint comme ceci WITH (INDEX(SPATIAL_idx_test)) je reçois l'erreur suivante:

MSG 8635, niveau 16, état 4, ligne 78
[.____] Le processeur de requête n'a pas pu produire un plan de requête pour une requête avec un indice d'index spatial. Raison: les index spatiaux ne prennent pas en charge le comparateur fourni dans le prédicat. Essayez de supprimer les indices d'index ou de supprimer SET FORCEPLAN. `

8
gotqn

Indépendamment de toute autre amélioration que vous apportez, veillez à tester l'impact sur les délais d'exécution spatiale d'activation des drapeaux de trace 6532, 6533 et 6534 (start-up uniquement). Ceux-ci activés Mises en œuvre spatiale du code natif. Service SQL Server 2012 Service Pack 3 ou SQL Server 2014 2 requis ( Article de support Microsoft ). La compilation native est ON par défaut de SQL Server 2016.

Pour STDistance L'indicateur de trace important est 6533 . Dans un simple test, ce temps d'exécution amélioré de 2100 ms à 150 ms sans utiliser d'index spatial sur l'instance SQL Server 2012 de l'ordinateur portable.

Exemple adapté de SQL 2016 - il fonctionne simplement plus vite: mise en œuvre (s) spatiale (s) native par Bob Ward):

Données de test

CREATE TABLE dbo.SpatialTest
(
    ID      integer NOT NULL IDENTITY(1,1) PRIMARY KEY CLUSTERED,
    Points  geography NOT NULL
);
GO
-- Insert random sample points
SET NOCOUNT ON;
GO
DECLARE @Point float = 1.1;

INSERT dbo.SpatialTest (Points) 
VALUES ('POINT(' + CAST(@Point AS varchar(20)) + ' ' + CAST(@Point AS varchar(20)) + ')' );

WHILE(SCOPE_IDENTITY() < 100000)
BEGIN
    SET @Point = @Point + Rand(SCOPE_IDENTITY());
    IF (@Point > 90.0)
    BEGIN
        SET @Point = -89.0 + Rand(SCOPE_IDENTITY());
    END;

    INSERT dbo.SpatialTest (Points) 
    VALUES ( 'POINT(' + CAST(@Point AS varchar(20)) + ' ' + CAST(@Point AS varchar(20)) + ')' );
END;

Requête de test

DBCC TRACEON (6533);
DBCC TRACESTATUS;
GO
DECLARE 
    @s datetime2 = SYSUTCDATETIME(),
    @g geography = 'POINT(1.0 80.5)';

SELECT [Matches] = COUNT_BIG(*) 
FROM dbo.SpatialTest AS ST
WHERE ST.Points.STDistance(@g) > 10000000.5
OPTION (MAXDOP 1, RECOMPILE);

SELECT [Elapsed STDistance Query (ms)] = DATEDIFF(MILLISECOND, @s, SYSUTCDATETIME());

Temps d'exécution moyens: 2100ms (drapeau de trace off); 150ms (drapeau de trace sur).

6
Paul White 9

La requête dans la question veut trouver des paires de points plus à moins de 200 mètres. Un tel type de requête n'est pas pris en charge par les index spatiaux de SQL Server.

Microsoft Docs Vue d'ensemble des index spatiaux Dites:

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

Dans certaines conditions, les index spatiaux prennent en charge les méthodes de géographie optionnelles suivantes: STIntersects(), STEquals() et STDistance(). Pour être soutenu par un indice 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) comparison_operator valid_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:

geography1.STIntersects(geography2) = 1

geography1.STEquals(geography2) = 1

geography1.STDistance(geography2) < number

geography1.STDistance(geography2) <= number

Comme vous pouvez le constater, la requête en question n'a pas l'une des formes prises en charge.

C'est pourquoi vous obtenez ce message d'erreur "Raison: les index spatiaux ne prennent pas en charge le comparateur fourni dans le prédicat" lorsque vous essayez de forcer l'utilisation de l'index.

Vous pouvez essayer de réécrire la requête pour créer un produit cartésien avec toutes les combinaisons de points et "soustraire" d'un ensemble de points à moins de 200 mètres, mais je doute qu'il soit plus efficace, même s'il utilisait l'index.


J'ai une table avec ~ 3000 rangées avec des emplacements géographiques de ma base de données et j'ai essayé la version simplifiée de la requête de la question à ce sujet. Ajout AND DS1.Point.STDistance(DS2.Point) IS NOT NULL n'était pas nécessaire, l'index a été utilisé sans cette clause.

L'indice n'a été utilisé que lorsque la comparaison était < 200.

Il n'a pas été utilisé lorsque la comparaison était > 200.

SELECT
    DS1.[Building ID]
FROM 
    [dbo].[tblPhysicalBuildings] AS DS1
    CROSS JOIN [dbo].[tblPhysicalBuildings] AS DS2
WHERE 
    DS1.LocationPoint.STDistance(DS2.LocationPoint) < 200
OPTION(RECOMPILE)
;

less

SELECT
    DS1.[Building ID]
FROM 
    [dbo].[tblPhysicalBuildings] AS DS1
    CROSS JOIN [dbo].[tblPhysicalBuildings] AS DS2
WHERE 
    DS1.LocationPoint.STDistance(DS2.LocationPoint) > 200
OPTION(RECOMPILE)
;

more

3
Vladimir Baranov

Pour 23 000 points, la requête est exécutée pendant 4-5 secondes. Comme je m'attends à avoir plus de valeurs, j'ai besoin de trouver une meilleure solution. [...] J'ai créé un indice spatial, mais l'optimiseur de requêtes ne l'utilise pas. Si j'utilise un indice comme ceci WITH (INDEX(SPATIAL_idx_test)) je reçois l'erreur suivante

La seule réponse ici ne fait aucune mention d'un index. Je voulais juste dire si vous migrez à PostGis , l'extension spatiale pour PostgreSQL qui est pratiquement la mise en œuvre de référence pour SIG et beaucoup plus complète en vedette, vous aurez ST_DWithin . Ceci sera utiliser un indice spatial.

CREATE INDEX ON datasource USING Gist( point );

SELECT DISTINCT ds1.id
FROM datasource AS ds1
WHERE EXISTS (
  SELECT 1
  FROM datasource ds2
  WHERE ds1.id = ds2.id
  AND NOT ST_DWithin( ds1.point, ds2.point, 200)
);

Cela utilisera l'index pour résoudre ST_DWithin, Puis invert cet ensemble. Vous pourrez peut-être faire encore mieux:

SELECT DISTINCT ds1.id
FROM datasource AS ds1
CROSS JOIN LATERAL (
  SELECT *
  FROM datasource AS ds2
  WHERE ds1.id = ds2=id
  ORDER BY ds1.point <=> ds.point DESC
  LIMIT 1;
) AS t2
WHERE ds1.id = t2.id
AND NOT ST_DWithin( ds1.point, t2.point, 200);

Cela ne sera exécuté que la distance de comparaison une fois, et cela utilisera Knn pour trier l'index.

Vous pouvez même le réduire à une recherche d'index avec btree_Gist .

CREATE EXTENSION btree_Gist;
CREATE INDEX ON datasource USING Gist( id, point );

PostgreSQL et PostGis sont un logiciel libre et open source.

J'ai répondu à une question comme celle-ci retour dans Janturary , mais je pense que le tampon de tolérance est probablement mieux. Si vous avez besoin de plus de précision, utilisez .buffer()

2
Evan Carroll

Une autre manière qui peut être plus rapide consiste à utiliser les méthodes - STITURSECTS et tamponwithtolérance pour vérifier si un point est dans une certaine distance d'une autre.

SELECT DISTINCT DS1.[ID]
FROM DataSource DS1
INNER JOIN DataSource DS2
    ON DS1.[ID] = DS2.[ID]
WHERE DS2.STIntersects(DS1.BufferWithTolerance(200, 0.9, 0)) = 1

Notez que ce tamponwithtolérance a une valeur de tolérance (dans mon exemple de 0,9) essentiellement une valeur de compromis entre la vitesse et la précision. Si vous voulez des résultats exacts, ce n'est probablement pas la méthode pour vous. Je semble aussi rappeler que STITRESSECTS est une méthode imprécise, mais je ne trouve aucune référence à ce moment-là pour le moment, alors peut-être que je me trompe à ce sujet.

0
paulH