J'ai obtenu 2 ensembles de points dans 2 tableaux distincts. Table_a a obtenu 100 000 points et table_b a obtenu 300 000 points. J'essaie de trouver les points les plus proches par rapport me trouver n'importe quel point de table_b qui est à moins de 50 mètres de tabla_a. Après avoir calculé la colonne d'automne, regroupez-les par colonne table_a a_id et retournez la valeur la plus élevée.
J'ai écrit une requête suivante qui répond à cette criteira
SELECT DISTINCT ON (a_id) *
FROM (
SELECT
table_b.b_id,
table_b.height - st_3ddistance(table_b.geom, table_a.geom) fall,
table_b.geom,
table_a.a_id
FROM table_a
INNER JOIN table_b ON _st_3ddwithin(table_a.geom, table_b.geom, 50)) a
WHERE fall >= 0
ORDER BY a_id, fall DESC;
J'ai ajouté des index de géométrie 3D:
CREATE INDEX table_a_geom ON table_a USING GIST (geom gist_geometry_ops_nd);
CREATE INDEX table_b_geom ON table_b USING GIST (geom gist_geometry_ops_nd);
Cependant, mon problème est que je ne peux pas faire de requête pour les utiliser. Le planificateur de requêtes continue de choisir un balayage de séquence lent. Je lance un test en changeant _st_3ddwithin avec st_3ddwithin , <<->> <50 , en créant 50 m de tampon et en intersectant , st_3ddistance <50 mais à chaque fois le planificateur choisit le balayage de séquence. Existe-t-il un moyen d'utiliser des index avec des performances supérieures ou de modifier la requête pour utiliser des index?
Mon plan de requête:
Unique (cost=10462593.70..10473018.43 rows=1 width=144)
-> Sort (cost=10462593.70..10467806.06 rows=2084945 width=144)
Sort Key: table_a.nmbayuid, ((table_b.height - st_3ddistance(table_b.geomgr, table_a.geom))) DESC
-> Nested Loop (cost=0.00..10243762.28 rows=2084945 width=144)
Join Filter: (_st_dwithin(table_a.geom, table_b.geomgr, '50'::double precision) AND ((table_b.height - st_3ddistance(table_b.geomgr, table_a.geom)) >= '0'::double precision))
-> Seq Scan on table_b (cost=0.00..1459.47 rows=47147 width=96)
-> Materialize (cost=0.00..10.97 rows=398 width=56)
-> Seq Scan on table_a (cost=0.00..8.98 rows=398 width=56)
la source
_ST
sont des fonctions internes appelées par PostGIS après filtrage avec un index. Si vous les appelez directement, l'index ne sera pas utilisé.Réponses:
Premièrement, comme cela a été noté dans les commentaires, le trait de soulignement avant la fonction ST, c'est-à-dire _ST_3DWithin, conduira à l'index non utilisé. Je ne trouve aucune mention récente à ce sujet, mais dans les anciens documents si vous recherchez, par exemple, _ST_Intersects, il indique:
EDIT: Comme précisé par @dbaston dans les commentaires, les fonctions avec le soulignement de tête sont des fonctions internes qui n'utilisent pas l'index lors de l'appel et cela continue d'être le cas (bien qu'il soit difficile à trouver dans les documents).
Votre requête pourrait éventuellement bénéficier de la syntaxe LATERAL JOIN, qui se prête bien à k problèmes de voisin le plus proche (kNN) comme celui-ci.
Cela vous permet de trouver les k géométries les plus proches de la table a (dans ce cas 1, en raison de LIMIT 1) à la table b, ordonnées par la distance 3D entre elles. Il est écrit à l'aide d'un LEFT JOIN, car il est concevable qu'il puisse y avoir des géométries dans le tableau a qui ne se trouvent pas à moins de 50 mètres du tableau b.
Les requêtes latérales vous permettent de référencer des colonnes de la clause FROM précédente, ce qui la rend plus puissante que les sous-requêtes standard, voir la documentation .
Je ne peux pas tester cela par rapport à vos données, mais lorsque j'ai exécuté des requêtes similaires, l'instruction EXPLAIN indique une utilisation correcte de l'index.
la source
Ce lien vers la documentation PostGIS recommande les étapes suivantes afin de garantir l'optimisation des index et du planificateur de requêtes:
Assurez-vous que des statistiques sont collectées sur le nombre et la distribution des valeurs dans une table, afin de fournir au planificateur de requêtes de meilleures informations pour prendre des décisions concernant l'utilisation de l'index. VACUUM ANALYZE calculera les deux.
Si le nettoyage par aspiration n'aide pas, vous pouvez temporairement forcer le planificateur à utiliser les informations d'index en utilisant la fonction set enable_seqscan sur off; commander. De cette façon, vous pouvez vérifier si le planificateur est capable de générer un plan de requête accéléré par index pour votre requête. Vous ne devez utiliser cette commande que pour le débogage: de manière générale, le planificateur sait mieux que vous quand utiliser les index. Une fois que vous avez exécuté votre requête, n'oubliez pas de réactiver ENABLE_SEQSCAN, afin que les autres requêtes utilisent normalement le planificateur.
Si désactivez enable_seqscan; aide votre requête à fonctionner, votre Postgres n'est probablement pas réglé pour votre matériel. Si vous trouvez que le planificateur se trompe sur le coût des analyses séquentielles vs index, essayez de réduire la valeur de random_page_cost dans postgresql.conf ou utilisez set random_page_cost à 1.1 ;. La valeur par défaut du paramètre est 4, essayez de le régler sur 1 (sur SSD) ou 2 (sur disques magnétiques rapides). La diminution de la valeur rend le planificateur plus enclin à utiliser les analyses d'index.
Si désactivez enable_seqscan; n'aide pas votre requête, il se peut que vous utilisiez une construction que Postgres n'est pas encore en mesure de démêler. Une sous-requête avec sélection en ligne en est un exemple - vous devez la réécrire dans le planificateur de formulaire pour optimiser, disons, une LATÉRALE JOIN.
Essayez donc d'abord les étapes 1 à 3 avant de réécrire votre requête pour utiliser les indices. Si cela ne fonctionne pas, vous pouvez essayer de modifier la requête.
Je crois (au mieux de ma capacité à fouetter SQL sans exécuter le code) que la requête ci-dessous retournera des résultats identiques aux vôtres, mais je ne sais pas si elle sera plus efficace.
la source
Si vous utilisez Postgres 10 (ou plus récent), je vous recommande fortement de charger vos données dans des tables parallèles.
Vous aurez probablement besoin de passer du temps à le régler (partitionnement des données et nombre de travailleurs), mais je pense que cela en vaut la peine. Théoriquement, KNN est hautement parallélisable, atteignant des complexités temporelles constantes, même O (1) si le nombre de travailleurs est égal au nombre d'éléments où une opération KNN sera calculée.
Des références pratiques sur le chargement des données et l'exécution des requêtes sont fournies ici . Il fournit des détails sur tunning plan (pour forcer plus de travailleurs à actionnées) ici . Il est important de noter que les scripts parallèles impliquent beaucoup de coordination des tâches, de sorte que la limite théorique extrême de fournir la parallélisation la plus extrême ne tient pas en pratique, en raison de la mise en réseau et d'autres caractéristiques de conception des systèmes.
la source