#1 Wed 20 November 2024 11:10
- ppluvinet
- Participant assidu
- Lieu: VALENCE
- Date d'inscription: 6 Aug 2007
- Messages: 617
UPDATE avec un WITH RECURSIVE
Bonjour,
J'ai une couche de polygones dont certains petits polygones ont un attribut NULL.
J'aimerai affecter à ces petits polygones la valeur d'un des polygones voisins. Comme certains sont entourés eux-mêmes de petits polygones avec des valeurs nulles, je souhaitais utiliser une fonction récursive qui ferait un UPDATE tant qu'il y a des valeurs nulles.
J'ai tenté la rédaction ci-après mais ca ne met à jour qu'une fois les polygones , la récursivité ne fonctionne pas et il reste des polygones avec des valeurs nulles qui ne sont pourtant pas isolés.
N'étant pas à l'aise avec les fonctions récursives, je fais appel à vos compétences
Code:
WITH RECURSIVE b AS ( SELECT * FROM poly WHERE attr is not null ) UPDATE poly a SET attr = b.attr FROM b WHERE a.attr is null AND st_intersects(a.geom,b.geom) ;
Merci d'avance pour vos suggestions,
Dernière modification par ppluvinet (Wed 20 November 2024 11:54)
Pascal PLUVINET
Hors ligne
#2 Wed 20 November 2024 12:00
- tumasgiu
- Membre
- Lieu: Ajaccio
- Date d'inscription: 5 Jul 2010
- Messages: 1160
Re: UPDATE avec un WITH RECURSIVE
Salut,
Il serait plus facile je pense d'écrire une fonction pl/psql pour arriver à ce que vous voulez faire.
Cela dit, voilà une idée de requête qui pourrait peut être fonctionner :
Code:
WITH RECURSIVE r(id, geom, attr) AS ( SELECT --partie de base de la requête recursive : on récupére tous les polygones sans attributs ayant un voisin avec attribut a.id, a.geom b.attr FROM poly a JOIN poly b on a.attr is null and b.attr is not null and st_intersects(a.geom, b.geom) UNION ALL SELECT --partie recursive : on récupére les enregistrements sans attributs voisins des enregistrements produits par le cas de base a.id, a.geom, r.attr FROM poly a RIGHT JOIN r ON a.id <> r.id --on veut éviter de reprendre les polygones déja traités and a.attr is null and r.attr is not null and st_intersects(a.geom, b.geom) ), x as ( select * from r LIMIT 1000 -- on mets généralement une limit au resultat produit par une requête recursive -- ca permet de tester qu'elle fonctionne et d'éviter qu'elle ne tourne indéfiniment -- si on a mal ecrit la requête ) UPDATE poly a SET attr = x.attr FROM (select id,max(attr) from x GROUP BY id) x WHERE a.id = x.id ;
Les CTE récursives se composent généralement d'une partie de base (dans le code la requête au dessus du UNION ALL) et d'une partie recursive (sous le UNION ALL). La CTE s'arrête une fois qu'elle ne produit plus de nouveaux resultat (une nouvelle itération de la partie recursive ne renvoie pas d'enregistrement).
Une difficulté de votre demande vient du fait qu'il peut potentiellement y avoir plusieurs candidats pour chaque polygone sans attribut. Les GROUP BY et LATERAL JOIN n'étant pas autorisés dans les CTE recursives, la solution que je propose utilise la CTE pour générer toutes les combinaisons possibles et la sous requête de la clause FROM de la commande UPDATE selectionne la valeur à attribuer (ici j'ai choisi la fonction max mais on peut en choisir une autre ou faire cela de façon non déterministe).
Je ne suis pas sûr des bonnes performances de la requête, si elle fonctione.
Dernière modification par tumasgiu (Wed 20 November 2024 12:05)
Hors ligne
#3 Wed 20 November 2024 12:22
- ppluvinet
- Participant assidu
- Lieu: VALENCE
- Date d'inscription: 6 Aug 2007
- Messages: 617
Re: UPDATE avec un WITH RECURSIVE
Merci beaucoup pour avoir pris le temps de me répondre.
Il faudra que je l'adapte encore car mon problème est un peu plus complexe. En effet, prendre la valeur du voisin est un peu trop simpliste !!
Je testerai et ferai un retour. Grand merci,
Pascal PLUVINET
Hors ligne
#4 Wed 20 November 2024 13:12
- Nicolas Ribot
- Membre
- Lieu: Toulouse
- Date d'inscription: 9 Sep 2005
- Messages: 1554
Re: UPDATE avec un WITH RECURSIVE
Bonjour,
Si les polygones à corriger intersectent des voisins dont ils doivent prendre une valeur, pourquoi ne pas utiliser st_clusterIntersecting ?
Vous aurez des groupes de polygones qui se touchent et dans chaque groupe, les polygones avec attribut null et des voisins avec l'attribut que voulez prendre.
(je sens bien que je n'ai pas tout compris à votre pb )
Nicolas
Hors ligne
#5 Wed 20 November 2024 13:15
- tumasgiu
- Membre
- Lieu: Ajaccio
- Date d'inscription: 5 Jul 2010
- Messages: 1160
Re: UPDATE avec un WITH RECURSIVE
@Nicolas,
de ce que j'ai cru comprendre il y a des polygones dont aucun voisin ne possède d'attribut, d'ou l'approche itérative.
Hors ligne
#6 Wed 20 November 2024 13:18
- Nicolas Ribot
- Membre
- Lieu: Toulouse
- Date d'inscription: 9 Sep 2005
- Messages: 1554
Re: UPDATE avec un WITH RECURSIVE
Vous auriez un exemple visuel ?
Nicolas
Hors ligne
#7 Wed 20 November 2024 13:21
- Nicolas Ribot
- Membre
- Lieu: Toulouse
- Date d'inscription: 9 Sep 2005
- Messages: 1554
Re: UPDATE avec un WITH RECURSIVE
@Nicolas,
de ce que j'ai cru comprendre il y a des polygones dont aucun voisin ne possède d'attribut, d'ou l'approche itérative.
Ok merci pour la précision. Si ce polygone n'a que des voisins sans attribut, alors le voisin du voisin sera connecté avec un polygone ayant un attribut, qui apparaitra donc dans le cluster et dont on pourra recupérér l'attribut, non ?
Nico
Hors ligne
#8 Wed 20 November 2024 13:32
- tumasgiu
- Membre
- Lieu: Ajaccio
- Date d'inscription: 5 Jul 2010
- Messages: 1160
Re: UPDATE avec un WITH RECURSIVE
tumasgiu a écrit:@Nicolas,
de ce que j'ai cru comprendre il y a des polygones dont aucun voisin ne possède d'attribut, d'ou l'approche itérative.
Ok merci pour la précision. Si ce polygone n'a que des voisins sans attribut, alors le voisin du voisin sera connecté avec un polygone ayant un attribut, qui apparaitra donc dans le cluster et dont on pourra recupérér l'attribut, non ?
Nico
Tout à fait. Je pense que l'approche avec st_clusterintersectings est valide. Après cela depend peut être de la façon dont on veut que les attributs se "diffusent" de voisin en voisin ? Il faudrait plus de précision sur le problème
En apparté je viens de voir que les fonctions de cluster ont maintenant une version utilisable dans les window function, .
Dernière modification par tumasgiu (Wed 20 November 2024 13:34)
Hors ligne
#9 Wed 20 November 2024 15:56
- Nicolas Ribot
- Membre
- Lieu: Toulouse
- Date d'inscription: 9 Sep 2005
- Messages: 1554
Re: UPDATE avec un WITH RECURSIVE
En apparté je viens de voir que les fonctions de cluster ont maintenant une version utilisable dans les window function, .
Ouiiii trop bien (et aussi les nouvelles fonctions de coverage, pour tester et cleaner ses couches)
Hors ligne
#10 Thu 21 November 2024 09:34
- ppluvinet
- Participant assidu
- Lieu: VALENCE
- Date d'inscription: 6 Aug 2007
- Messages: 617
Re: UPDATE avec un WITH RECURSIVE
Bonjour,
Je n'ai pas encore testé la solution avec WITH RECURSIVE. Je vais m'y mettre ce matin !
Pour mieux expliquer mon problème de base : A l'intérieur d'un même ensemble de polygones jointifs, je peux avoir des grands polygones qui ont des valeurs d'attributs différents et une multitude de petits polygones avec des valeurs nulles. Certains de ces petits polygones sont entourés de petits polygones avec des valeurs nulles aussi. Du coup, je doute qu'on puisse utiliser st_clusterintersectings (que je n'ai jamais utilisé par ailleurs ! ).
Ce que je souhaitais c'est de boucler pour que les valeurs nulles soient, petit à petit, remplacées par une des valeurs des voisins.
Si je veux aller au bout de ma démarche, je dois exécuter cette "diffusion récursive" à plusieurs endroits dans mon script. Peut-être qu'une fonction plpsql serait plus appropriées ? Mais là aussi , il faut que je me repenche sur ce type de prog, car ca fait quelques années que je n'ai pas pratiqué ! Si vous avez quelque chose de simple pour démarrer ?
Merci d'avance,
Pascal PLUVINET
Hors ligne
#11 Thu 21 November 2024 10:38
- tumasgiu
- Membre
- Lieu: Ajaccio
- Date d'inscription: 5 Jul 2010
- Messages: 1160
Re: UPDATE avec un WITH RECURSIVE
Si vous n'avez pas besoin de choisir quelle valeur d'attribut affecter à vos petits polygones, l'approche de Nicolas sera beaucoup moins casse-tête et sans doute plus rapide :
Pour chaque cluster de polygones renvoyé par la fonction st_clusterintersecting (elle renvoie un tableau de geometrycollection ou chaque entrée est un cluster), vous récupérez l'attribut d'un des polygones du cluster et vous l'affecter aux autres polygones sans attributs du cluster.
Hors ligne
#12 Thu 21 November 2024 11:01
- Nicolas Ribot
- Membre
- Lieu: Toulouse
- Date d'inscription: 9 Sep 2005
- Messages: 1554
Re: UPDATE avec un WITH RECURSIVE
Pour mieux expliquer mon problème de base : A l'intérieur d'un même ensemble de polygones jointifs, je peux avoir des grands polygones qui ont des valeurs d'attributs différents et une multitude de petits polygones avec des valeurs nulles. Certains de ces petits polygones sont entourés de petits polygones avec des valeurs nulles aussi. Du coup, je doute qu'on puisse utiliser st_clusterintersectings (que je n'ai jamais utilisé par ailleurs ! ).
Ce que je souhaitais c'est de boucler pour que les valeurs nulles soient, petit à petit, remplacées par une des valeurs des voisins.
Un sample, un sample !
Nico
Hors ligne
#13 Thu 21 November 2024 11:15
- ppluvinet
- Participant assidu
- Lieu: VALENCE
- Date d'inscription: 6 Aug 2007
- Messages: 617
Re: UPDATE avec un WITH RECURSIVE
Le WITH RECURSIVE de tumasgiu fonctionne mais il est long et je n'ai pas manière de savoir où j'en suis. De plus, il y a toujours quelques polygones qui ont des valeurs nulles et il faudrait mettre une valeur bien plus importante au LIMIT.
J'ai donc tenté d'écrire une fonction qui semble fonctionner :
Code:
CREATE OR REPLACE function diffusion() returns text LANGUAGE plpgsql AS $$ DECLARE re text default ''; i INT := 1; BeforeMaj INT := 1; AfterMaj INT := 0; NBPOLYUPDATED INT := 0; BEGIN WHILE AfterMaj < BeforeMaj LOOP RAISE NOTICE 'i: %', i; BeforeMaj := (select count(*) from poly where attr is null); execute 'update poly a set attr = b.attr from poly b where a.attr is null and b.attr is not null and st_intersects(a.geom,b.geom)' ; AfterMaj := (select count(*) from poly where attr is null); i := i+1; NBPOLYUPDATED := NBPOLYUPDATED + BeforeMaj - AfterMaj; RAISE NOTICE 'UPDATE: %', BeforeMaj - AfterMaj; END LOOP; re := 'NB TOTAL UPDATED: '|| NBPOLYUPDATED ; return re; END; $$ ; select diffusion();
Je vais pouvoir facilement complexifier le WHERE du UPDATE en contraignant d'avantage le choix du voisin.
Merci encore,
Pascal PLUVINET
Hors ligne
#14 Thu 21 November 2024 12:00
- tumasgiu
- Membre
- Lieu: Ajaccio
- Date d'inscription: 5 Jul 2010
- Messages: 1160
Re: UPDATE avec un WITH RECURSIVE
Le WITH RECURSIVE de tumasgiu fonctionne mais il est long
Helas c'était à craindre.
il faudrait mettre une valeur bien plus importante au LIMIT.
Vous pouvez tout à fait augmenter le nombe d'enregistrement à retourner. Le limit n'est la que pour vous prémunir d'une possible requête qui ne terminerait jamais et saturerait votre serveur.
Votre code est bien, mais je pense que vous pouvez le simplifier comme suit (non testé) :
Code:
CREATE OR REPLACE function diffusion() returns text LANGUAGE plpgsql AS $$ DECLARE updated := 1 NBPOLYUPDATED INT := 0; BEGIN WHILE updated > 0 LOOP RAISE NOTICE 'i: %', i; EXECUTE 'update poly a set attr = b.attr from poly b where a.attr is null and b.attr is not null and st_intersects(a.geom,b.geom)' ; GET DIAGNOSTICS updated := ROW_COUNT ; --https://www.postgresql.org/docs/current/plpgsql-statements.html#PLPGSQL-STATEMENTS-DIAGNOSTICS NBPOLYUPDATED := updated + NBPOLYUPDATED ; END LOOP; return 'NB TOTAL UPDATED: '|| NBPOLYUPDATED ; END; $$ ; select diffusion();
Il n'y a plus qu'a tester la version de Nicolas
Dernière modification par tumasgiu (Thu 21 November 2024 14:20)
Hors ligne
#15 Mon 25 November 2024 15:54
- ChristopheV
- Membre
- Lieu: Ajaccio
- Date d'inscription: 7 Sep 2005
- Messages: 3199
- Site web
Re: UPDATE avec un WITH RECURSIVE
Bonjour,
En utilisant la topologie sous PostGis le problème se simplifie.
Très facile de trouver les faces voisines possédant une valeur = Null à une face donnée.
Ou inversement de connaître la voisine attribut non Null de face attribut=Null.
Une autre approche non testée, et l'utilisation des LATERAL JOIN.
Pour chaque polygone "non null" trouver les polygones adjacents de valeur nulle. Un st_touches() AND attribut isnull ou plutôt st_overlaps() AND attribut isnull.
[Edit] Je n'avais pas vu la condition "certains polygones de valeur NULL sont entourés de polygones valeurs Null". Dans ce cadre (je ne suis pas sur de modéliser tous les cas d'utilisation) je chercherais tous les polygones de valeur null adjacents, j'en ferais un multypolygone, ensuite je cherche à quel polyone de valeur non nulle ce multipolygon est adjacent, pour affecter le bon attribut à chaque partie du multipolygon. [Edit]
Dernière modification par ChristopheV (Tue 26 November 2024 12:09)
Christophe
L'avantage d'être une île c'est d'être une terre topologiquement close
Hors ligne