SQL Server dispose d’index spatiaux qui nécessitent l’utilisation d’une « bounding box » pour le type geometry. Une « bounding box » est en fait un rectangle qui délimite la surface à indexer. Mais comment déterminer les limites de cette « boite » ? Cet article vous propose une procédure stockée capable de calculer les meilleurs paramètres de ces « bounding box » et vous donne le code de création des index spatiaux à ajouter à votre base.
Dans le principe, tous les points de toutes vos géométries dans une même colonne de table devraient être situés dans la bounding box de l’index pour que certaines recherches bénéficient de l’indexation. Pour ce faire il faut déterminer le point le plus bas, le plus haut (en y) le plus à gauche et le plus à droite (en x) de l’ensemble des points formant toutes vos géométries.
Pour effectuer ce calcul il existe une astuce qui consiste à calculer l’enveloppe d’une géométrie, comme le montre l’image suivante :
Notez que le calcul des limites de Paris a été effectué par concaténation des polygones des différentes arrondissements dans une premier temps (geometry::UnionAggregate(GEOM)).
À partir de là on peut obtenir les valeurs min et max de X et Y, car, comme le rectangle est formé de 4 points, il suffit de prendre les coordonnées de X et Y du point 1 et du point 3, les points étant spécifiées par chainage !
Dès lors la méthode pour obtenir les coordonnées de la bounding box est d’obtenir les valeurs de X et Y comme ceci :
Mais il faut concaténer toutes les entrées de toutes les géométries de la colonnes à l’aide de la fonction EnvelopeAggregate.
la requête finale ressembles à celle-ci (calcul de la bounding box pour toutes les communes de la France métropolitaine) :
(
SELECT geometry::EnvelopeAggregate(GEOM) AS GEO
FROM [S_GEO].[COMMUNE]
)
SELECT geometry::EnvelopeAggregate(GEO).STPointN(1).STX AS MIN_X,
geometry::EnvelopeAggregate(GEO).STPointN(3).STX AS MAX_X,
geometry::EnvelopeAggregate(GEO).STPointN(1).STY AS MIN_Y,
geometry::EnvelopeAggregate(GEO).STPointN(3).STY AS MAX_Y
FROM T
Au final nous pouvons automatiser ce calcul pour toutes les colonnes geometry de toute la base. Ceci conduit à la procédures suivantes :
AS
DECLARE @T TABLE (SCH sysname, TBL sysname, COL sysname, XMIN FLOAT, XMAX FLOAT, YMIN FLOAT, YMAX FLOAT);
-- le delate doit être 1 (bornes strictes) ou supérieur à 1
IF @DELTA < 1 OR @DELTA IS NULL
SET @DELTA = 1;
-- recherches des colonnes spatiales non indexées de type GEOMETRY
INSERT INTO @T (SCH, TBL, COL)
SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE DATA_TYPE = 'geometry'
EXCEPT
SELECT s.name, o.name, c.name
FROM sys.objects AS o
JOIN sys.indexes AS i
ON o.object_id = i.object_id
JOIN sys.schemas AS s
ON o.schema_id = s.schema_id
JOIN sys.spatial_indexes AS si
ON i.object_id = si.object_id
AND i.index_id = si.index_id
JOIN sys.index_columns AS ic
ON i.object_id = ic.object_id
AND i.index_id = ic.index_id
JOIN sys.COLUMNS AS c
ON ic.object_id = c.object_id
AND ic.column_id = c.column_id
JOIN sys.systypes AS t
ON c.system_type_id = t.TYPE
WHERE t.name = 'geometry';
DECLARE @TXY TABLE (XMIN FLOAT, XMAX FLOAT, YMIN FLOAT, YMAX FLOAT);
DECLARE @TBL sysname, @SCH sysname, @COL sysname,
@XMIN FLOAT, @XMAX FLOAT, @YMIN FLOAT, @YMAX FLOAT,
@SQL NVARCHAR(MAX);
DECLARE C CURSOR
LOCAL
FORWARD_ONLY
STATIC
READ_ONLY
FOR
SELECT SCH, TBL, COL
FROM @T;
OPEN C;
FETCH C INTO @SCH, @TBL, @COL;
WHILE @@FETCH_STATUS = 0
BEGIN
DELETE FROM @TXY;
SET @SQL = N'WITH T AS (SELECT geometry::EnvelopeAggregate([' + @COL + N']) AS GEO FROM [' + @SCH + '].[' + @TBL + ']) '
+ N'SELECT geometry::EnvelopeAggregate(GEO).STPointN(1).STX / ' + CAST(@DELTA AS VARCHAR(16)) + N' AS MIN_X, '
+ N'geometry::EnvelopeAggregate(GEO).STPointN(3).STX * ' + CAST(@DELTA AS VARCHAR(16)) + N' AS MAX_X, '
+ N'geometry::EnvelopeAggregate(GEO).STPointN(1).STY / ' + CAST(@DELTA AS VARCHAR(16)) + N' AS MIN_Y, '
+ N'geometry::EnvelopeAggregate(GEO).STPointN(3).STY * ' + CAST(@DELTA AS VARCHAR(16)) + N' AS MAX_Y '
+ N'FROM T;'
INSERT INTO @TXY
EXEC (@SQL);
UPDATE T
SET XMIN = TT.XMIN, XMAX = TT.XMAX, YMIN = TT.YMIN, YMAX = TT.YMAX
FROM @T AS T
CROSS JOIN @TXY AS TT
WHERE SCH = @SCH
AND TBL = @TBL
AND COL = @COL;
FETCH C INTO @SCH, @TBL, @COL;
END
CLOSE C;
DEALLOCATE C;
SELECT N'CREATE SPATIAL INDEX XS_' + LEFT(SCH, 16) + N'_' + LEFT(TBL, 30) + N'_' + LEFT(COL, 30) + N'_'
+ REPLACE(CAST(NEWID() AS NVARCHAR(38)), N'-', N'_') + '_' + CONVERT(CHAR(8), GETDATE(), 112)
+ NCHAR(13) + NCHAR(10)
+ N' ON [' + SCH + N'].[' + TBL + '] ([' + COL + N']) '
+ NCHAR(13) + NCHAR(10)
+ N' WITH ( BOUNDING_BOX = ('
+ CAST( XMIN AS NVARCHAR(32)) + N', '
+ CAST( YMIN AS NVARCHAR(32)) + N', '
+ CAST( XMAX AS NVARCHAR(32)) + N', '
+ CAST( YMAX AS NVARCHAR(32)) + N') );'
FROM @T;
GO
Le paramètre @DELTA sert à rajouter un peu de "mou" sur la périphérie de la bounding box et doit être égale à 1 (pas de "mou") ou supérieur.
par exemple avec 1.1, on ajoute 10% de distance en périphérie de la bounding box.
LE CODE * LE CODE * LE CODE * LE CODE * LE CODE * LE CODE * LE CODE * LE CODE * LE CODE
Le code !
Expert S.G.B.D relationnelles et langage S.Q.L
Moste Valuable Professionnal Microsoft SQL Server
Société SQLspot : modélisation, conseil, formation,
optimisation, audit, tuning, administration SGBDR
Enseignant: CNAM PACA, ISEN Toulon, CESI Aix en Prov.
L’entreprise SQL Spot
Le site web sur le SQL et les SGBDR