4.6. Requêtes complexes


La raison d'être des fonctionnalités spatiales des base de données est de vous permettre d'effectuer des requêtes qui requièrent habituellement des outils SIG bureautiques. Utiliser PostGIS de manière efficace nécessite de connaitre qu'elles sont les fonctions spatiales disponibles et de s'assurer que les indexes appropriés sont en place pour obtenir de bonne performances.

 Table des matières

4.6.1. Tirer avantage des indexes.
4.6.2. Exemple de SQL spatial
4.6.2.1. Quelle est la longueur totale en kilomètres de toutes les routes ?
4.6.2.2. Quelle est l'aire en hectare de la ville Prince George ?
4.6.2.3. Quelle est la plus grande municipalité (aire) de la province ?
4.6.2.4. Quelle est la longueur des routes totalement contenues dans chaque municipalités ?
4.6.2.5. Créer une nouvelle table contenant toutes les routes de la ville de Prince George.
4.6.2.6. Quelle est la longueur en kilomètre de "Douglas St" à Victoria ?

4.6.1. Tirer avantage des indexes.

Lorsque vous construisez une requête il est important de se souvenir que seuls les opérateurs du cadre-limite comme && tirent avantage des indexes spatiaux GIST. Les fonctions comme distance() ne peuvent pas utiliser les indexes pour optimiser leur opérations. Par exemple, la requête suivante pourrait être légèrement lente sur une grosse table :

SELECT the_geom FROM geom_table WHERE distance( the_geom, GeomFromText( 'POINT(100000 200000)', -1 ) ) < 100

Cette requête sélectionne toutes les géométries de geom_table qui sont dans un rayon de 100 unités du point (100000, 200000). Elle est lente parce qu'elle calcule la distance entre chaque point de la table et le point que nous avons spécifié, par exemple un calcul de distance() pour chaque enregistrement de la table. Nous pouvons éviter cela en utilisant l'opérateur && pour réduire le nombre de distance requit :

SELECT the_geom FROM geom_table
WHERE the_geom && 'BOX3D(90900 190900, 100100 200100)'::box3d
AND distance( the_geom, GeomFromText( 'POINT(100000 200000)', -1 ) ) < 100

Cette requête sélectionne les même géométries, mais il le fait de manière plus efficace. Supposons qu'il existe un indexe GiST sur the_geom, le planificateur de requêtes constatera qu'il peut utiliser l'indexe pour réduire le nombre de ligne avant de calculer le résultat de la fonction distance(). Notez que la géométrie BOX3D qui est utilisé dans l'opération && est un cadre carré de 200 unités centré sur le point original - c'est notre "cadre de requête". L'opérateur && utilise l'indexe pour réduire rapidement l'ensemble résultant aux géométries qui ont un cadre limite qui recouvre le "cadre de requête". En supposant que notre "cadre de requête" est plus petit que l'extension de la totalité de la table, cela réduira considérablement le nombre de calculs de distance qui devra être effectué.

Note : depuis PostGIS 1.3.0, la plupart des fonctions relationnelles, à l'exception de ST_Disjoint and ST_Relate, utilisent implicitement l'opérateur de superposition du cadre limite.

haut de la page | table des matières

4.6.2. Exemple de SQL spatial

Les exemples de cette section utiliseront deux tables, une table de routes linéaires, et une table de limite polygonales des municipalités. La définition pour la table bc_routes est la suivante :

Column   | Type              | Description
---------+-------------------+-------------------
gid      | integer           | Unique ID
name     | character varying | Road Name
the_geom | geometry          | Location Geometry (Linestring)

La définition pour la table bc_minicipalitees est la suivante :

Column     | Type              | Description
-----------+-------------------+-------------------
gid        | integer           | Unique ID
code       | integer           | Unique ID
name       | character varying | City / Town Name
the_geom   | geometry          | Location Geometry (Polygon)

haut de la page | table des matières

4.6.2.1. Quelle est la longueur totale en kilomètres de toutes les routes ?

Vous pouvez répondre à cette question avec une requête SQL simple :

postgis=# SELECT sum(length(the_geom))/1000 AS longueur_routes_en_km FROM bc_routes :

longueur_routes_en_km
------------------
70842.1243039643
(1 row)

haut de la page | table des matières

4.6.2.2. Quelle est l'aire en hectare de la ville Prince George ?

Cette requête combine une condition attributaire (sur le nom de la municipalité) avec un calcul spatial (de l'aire) :

postgis=# SELECT area(the_geom)/10000 AS hectares FROM bc_municipalite WHERE name = 'PRINCE GEORGE';
hectares
------------------
32657.9103824927
(1 row)
haut de la page | table des matières

4.6.2.3. Quelle est la plus grande municipalité (aire) de la province ?

Cette requête utilise une mesure spatiale comme condition. Il y a plusieurs manières d'approcher ce problème, mais la plus efficace est la suivante :

postgis=# SELECT name, area(the_geom)/10000 AS hectares
          FROM bc_municipalite
          ORDER BY hectares DESC LIMIT 1;

     name      |    hectares
---------------+-----------------
TUMBLER RIDGE  | 155020.02556131
(1 row)

Vous remarquerez que pour répondre à cette question nous devons calculer l'aire de tous les polygones. Si nous faisons souvent cela, il serait judicieux d'ajouter une colonne "aire" à la table que nous pourrions alors indexer pour plus de performance. En ordonnant le résultat par ordre décroissant, et en utilisant la commende LIMIT de PostgreSQL nous pouvons aisément récupérer la plus grande valeur sans avoir à utiliser de fonction d'agrégation comme max().

haut de la page | table des matières

4.6.2.4. Quelle est la longueur des routes totalement contenues dans chaque municipalités ?

Ceci est un exemple de "jointure spatiale', parce que nous utilisons les données de deux tables (en faisant une jointure) mais en utilisant une condition d'interaction spatiale ("contenue") comme condition de jointure au lieu d'utiliser l'approche relationnelle habituelle de jointure sur une clef commune :

postgis=# SELECT m.name, sum(length(r.the_geom))/1000 as roads_km
         FROM bc_roads AS r,bc_municipality AS m
         WHERE r.the_geom && m.the_geom
               AND contains(m.the_geom,r.the_geom)
         GROUP BY m.name
         ORDER BY roads_km;

           name             |    roads_km
----------------------------+------------------
SURREY                      | 1539.47553551242
VANCOUVER                   | 1450.33093486576
LANGLEY DISTRICT            | 833.793392535662
BURNABY                     | 773.769091404338
PRINCE GEORGE               | 694.37554369147
...

Cette requête dure un moment, parce que chaque route de la table est disponible dans le résultat final (environ 250K routes pour notre table d'exemple). Pour des couvertures plus petites (quelques milliers ou centaines d'enregistrements) la réponse peut être très rapide.

haut de la page | table des matières

4.6.2.5. Créer une nouvelle table contenant toutes les routes de la ville de Prince George.

Ceci est un exemple de "couverture", qui extrait les données de deux tables et retourne une nouvelle table qui contient l'ensemble des résultantes incluses ou partiellement coupées. Contrairement à la "jointure spatiale" présentée précédemment, cette requête crée de nouvelles géométries. Une couverture est comme une jointure spatiale turbo-cached, et est utile pour un travail d'analyse plus précis :

postgis=# CREATE TABLE pg_roads as
SELECT intersection(r.the_geom, m.the_geom) AS intersection_geom,
length(r.the_geom) AS rd_orig_length,
r.*
FROM bc_roads AS r, bc_municipality AS m
WHERE r.the_geom && m.the_geom
AND intersects(r.the_geom, m.the_geom)
AND m.name = 'PRINCE GEORGE';
haut de la page | table des matières

4.6.2.6. Quelle est la longueur en kilomètre de "Douglas St" à Victoria ?

postgis=# SELECT gid, name, area(the_geom) AS area
          FROM bc_municipality
          WHERE nrings(the_geom) > 1
          ORDER BY area DESC LIMIT 1;
gid  | name         | area
-----+--------------+------------------
12   | SPALLUMCHEEN | 257374619.430216
(1 row)
haut de la page | table des matières