Pourquoi st_intersects est-il plus rapide que &&

10

C'est un tableau de points. ~ 1 million d'enregistrements

SELECT COUNT(*) as value FROM alasarr_social_mv s; 
Output: 976270

Il semble que st_intersects force à utiliser les index spatiaux, mais && ne le fait pas.

Échantillon utilisant ST_Intersects(282ms)

SELECT COUNT(*) as value
FROM alasarr_social_mv 
WHERE ST_Intersects(
  the_geom_webmercator, 
  ST_MakeEnvelope(-410961,4920492,-402305,4926887,3857)
)


Aggregate  (cost=34370.18..34370.19 rows=1 width=0) (actual time=282.715..282.715 rows=1 loops=1)
  ->  Bitmap Heap Scan on alasarr_social_mv s  (cost=5572.17..34339.84 rows=60683 width=0) (actual time=21.574..240.195 rows=178010 loops=1)
        Recheck Cond: (the_geom_webmercator && '0103000020110F0000010000000500000000000000441519C1000000002BC5524100000000441519C1000000C069CB524100000000048E18C1000000C069CB524100000000048E18C1000000002BC5524100000000441519C1000000002BC55241'::geometry)
        Filter: _st_intersects(the_geom_webmercator, '0103000020110F0000010000000500000000000000441519C1000000002BC5524100000000441519C1000000C069CB524100000000048E18C1000000C069CB524100000000048E18C1000000002BC5524100000000441519C1000000002BC55241'::geometry)
        Heap Blocks: exact=4848
        ->  Bitmap Index Scan on alasarr_social_mv_gix  (cost=0.00..5569.13 rows=182050 width=0) (actual time=20.836..20.836 rows=178010 loops=1)
              Index Cond: (the_geom_webmercator && '0103000020110F0000010000000500000000000000441519C1000000002BC5524100000000441519C1000000C069CB524100000000048E18C1000000C069CB524100000000048E18C1000000002BC5524100000000441519C1000000002BC55241'::geometry)
Planning time: 0.192 ms
Execution time: 282.758 ms

Échantillon utilisant &&(414 ms)

SELECT COUNT(*) as value
FROM alasarr_social_mv  
WHERE the_geom_webmercator && 
  ST_MakeEnvelope(-410961,4920492,-402305,4926887,3857)

Aggregate  (cost=22535.97..22535.97 rows=1 width=0) (actual time=414.314..414.314 rows=1 loops=1)
  ->  Seq Scan on alasarr_social_mv  (cost=0.00..22444.94 rows=182050 width=0) (actual time=0.017..378.427 rows=178010 loops=1)
        Filter: (the_geom_webmercator && '0103000020110F0000010000000500000000000000441519C1000000002BC5524100000000441519C1000000C069CB524100000000048E18C1000000C069CB524100000000048E18C1000000002BC5524100000000441519C1000000002BC55241'::geometry)
        Rows Removed by Filter: 798260
Planning time: 0.134 ms
Execution time: 414.343 ms

Version PostGIS

POSTGIS="2.2.2" GEOS="3.5.0-CAPI-1.9.0 r4084" PROJ="Rel. 4.8.0, 6 March 2012" GDAL="GDAL 1.11.0, released 2014/04/16" LIBXML="2.7.8" LIBJSON="UNKNOWN" (core procs from "2.2.2" need upgrade) RASTER (raster procs from "2.2.2" need upgrade)  alasarr 2 mins ago
hélasarr
la source
2
Ne collez pas de captures d'écran de texte dans la question. Pouvez-vous les copier et les coller en tant que code? Je ne peux pas les lire pour vous aider.
Evan Carroll
Terminé. Je pense que c'est un peu mieux maintenant
alasarr
J'ai essayé de rétroporter vos nouveaux plans de requête dans cela. N'hésitez pas à mettre à jour les plans mais essayez de garder le style.
Evan Carroll
4
Jetez un oeil à cette question . L'opérateur && effectue en fait une requête de boîte englobante, tandis que ST_Intersects utilise une requête de boîte englobante pour déterminer les géométries à tester pour la comparaison réelle - vous vous attendez donc à ce que && soit plus rapide. Cependant, il est probable que l'utilisation de ST_MakeEnvelope à droite de && incite le planificateur de requêtes à choisir une analyse complète de la table pour une raison quelconque (comme cela ressort clairement de l'explication). Essayez d'abord de créer la géométrie, dans un CTE, et voyez si vous pouvez "tromper" l'optimiseur.
John Powell
Tu as raison! cela fonctionne à l'intérieur d'un CTE
alasarr

Réponses:

16

Ce type de constat revient assez souvent, et il est un peu obscur, il vaut donc la peine de le répéter. Si vous définissez une géométrie dans une fonction qui l'utilise, comme ST_Intersects ou && (que ST_Intersects utilise sous le capot), le planificateur de requêtes choisit une analyse complète de la table, car "il" n'a aucune connaissance du résultat de la création de la géométrie. , c'est-à-dire ST_MakeEnvelope dans ce cas. Si vous définissez la géométrie à vérifier pour l'intersection dans un CTE, l'optimiseur traite une quantité connue et utilise un index spatial, s'il est disponible.

Donc, réécrivez votre requête en:

WITH test_geom (geom) AS 
   (SELECT ST_MakeEnvelope(-410961,4920492,-402305,4926887,3857))
  SELECT COUNT(*) as value
    FROM alasarr_social_mv mv, test_geom tg 
   WHERE ST_Intersects(mv.the_geom_webmercator, tg.geom)

va maintenant utiliser un index spatial. De même, && utilisera désormais un index pour rechercher une boîte englobante et, (même si je ne peux pas tester par rapport à vos données), devrait être plus rapide que ST_Intersects.

Fait intéressant, dans votre requête, ST_Intersects utilise un index de scan bitmap (et non un index), tandis que && n'utilise aucun index. Ainsi, les deux requêtes seront plus rapides avec le CTE, mais && devrait maintenant être plus rapide que ST_Intersects.

Il y a plus d'explications sur ce qui se passe dans cette question et ses réponses / commentaires .

EDIT : Pour rendre cela explicite, si vous regardez la définition de ST_Intersects dans postgis.sql (qui est appelée par CREATE EXTENSION postgiset trouvée dans le répertoire contrib de votre installation Postgres), vous verrez:

---- Inlines index magic
CREATE OR REPLACE FUNCTION ST_Intersects(geom1 geometry, geom2 geometry)
    RETURNS boolean
    AS 'SELECT $1 OPERATOR(&&) $2 AND _ST_Intersects($1,$2)'
    LANGUAGE 'sql' IMMUTABLE ;

y compris le commentaire: index en ligne magique.

John Powell
la source
1
Je ne pense pas que ST_Intersects utilise && sous le capot.
Evan Carroll
4
@EvanCarroll, vérifiez ma modification. Jetez un œil à postgis.sql, où les fonctions sont définies, et cela devrait être plus clair.
John Powell