Vous n'êtes pas identifié(e).
Bonjour,
Je travaille sous PG9.6 environnement linux.
J'ai une fonction qui effectue des modifications de table avec un parcours de boucle.
En gros: je crée une table temporaire contenant des données pour simplifier disons :
La table T1 contient idt1, dateDeb_t1, dateFin_t1 (la table est remplie de données)
Par ailleurs, j'ai aussi une autre table physique non temporaire T2, avec T2 = idt2, dateDeb_t2 et dateFin_T2
Le principe est le suivant :
Je parcours les données de T1 comme suit: à chaque itération (ligne d'enregistrement) trouvée dans T1 je vérifie si il existe une certaine correspondance avec des lignes de T2 : s'il y a correspondance alors je modife dateDeb_T2 par dateFin_T1 pour la ligne (itération) en question idt1.
Pour simplifier on a comme suit :
FOR x,y,z IN SELECT idt1, dateDeb_t1, dateFin_t1 FROM T1
LOOP -- Je parcours une à une les lignes de T1
FOR a,b,c IN SELECT idt2, dateDeb_t2, dateFin_T2 FROM T2 WHERE dateDeb_t2 >= y AND dateDeb_t2 < z-- ici je verifie s'il existe la "correspondance"
LOOP
-- Pour chaque ligne T2 comprise dans la ligne en cours T1 : modifier dateDeb_T2 par dateFin_T1
-- C'est ici le cas intéressant : le pourquoi du comment ???
UPDATE T2 SET dateDeb_T2 = z
WHERE idt1 = a;
END LOOP;
END LOOP;
Le souci est que en exécutant cette fonction, elle tourne mais en vérifiant, les données ne sont pas à jour durant la boucle. C'est à dire je modifie dans le cas "pourquoi du comment" dateDeb_t2 mais pourtant au fur et à mesure du passage d'itération dans les boucles les nouvelles dateDeb_t2 ne sont pas à jour mais reste toujours la valeur initiale.
Sauriez vous pourquoi et comment corriger le fait que les valeurs initiales ne sont pas modifiées au fur et à mesure de la boucle.
Je précise que le type de dateDeb et dateFin joué ici est du timestamp without time zone, est qu'il y a lien la -dessus ?
Merci de votre attention et espérant qu'il y aura des réponses
Hors ligne
Je ne suis pas sur de comprendre. Est-ce que votre question est de savoir pourquoi la requête utilisée dans la 2nde boucle ne se met pas à jour à chaque itération, au fur et à mesure des modifications effectuée dans la 2ème boucle ? Si c'est le cas, c'est contraire au principe de cohérence, où chaque requête effectuée garantit fournir un résultat cohérent, malgré toutes les modifications concurrentes.
Julien.
https://rjuju.github.io/
Hors ligne
Oui, je veux que lorsqu'on fait une modification dans la boucle , cette modification sera tout de suite aperçu lors des prochaines itérations dans la boucle.
Pour plus d'explication, une autre façon d'approche c'est que :
on a une valeur initiale par exemple : x = 2 , avant d'entrer dans la boucle. Puis on entre en boucle FOR : x vaut 2 toujours , disons maintenant que durant les traitements effectués dans la boucle FOR, on fait un update de x , x est maintenant égale à 3 normalement (c'est ce qu'on veut); mais, on constate que durant le parcours de boucle x est toujours = 2 mais pas à 3. Les modifications ne sont elles pas prise en compte instantanément dans une boucle ? Ou faudrait il attendre que le boucle soit fini pour prendre en compte les modifications ? Comment on peut contourner cela ?
Hors ligne
Pour ajouter je veux que les modifications faites dans la 2ème boucle, soient perceptible au niveau de la 1ère boucle :
itération i=1 de la première boucle , on fait une modification dans la 2ème boucle, donc dans la 2ième itération (et plus) de la première boucle le update fait (dans 2ème boucle) lors de la première itération (1ère boucle) soit pris en compte (valeur mise à jour dans les itérations qui suivent)
Normalement çà devrait être çà non ?
Hors ligne
En guise d'information:
Pour contourner le problème j'ai du à chaque update fait dans la 2ème boucle > tracer la ligne modifiée par son id puis la valeur modifiée. Et au passage de boucle, vérifier si la ligne traitée dans la boucle figure dans le traçage, si oui mettre à jour l'élément modifiée par sa bonne valeur. Re vérifier si le test dans 2 ème boucle coincide toujours avec les nouvelles valeurs. Du coup les éléments sont à jour au fur et à mesure des itérations.
Hors ligne
Pour ajouter je veux que les modifications faites dans la 2ème boucle, soient perceptible au niveau de la 1ère boucle :
Ce n'est toujours pas clair parce que l'expression "pris en compte" reste à interpréter. Il serait peut être utile d'ajouter des raise notice dans ce code pour pouvoir dire à tel moment la valeur de telle variable est X alors qu'elle devrait être Y.
Je suspecte qu'il y a une confusion sur le fait que le SELECT derrière une boucle FOR n'est exécuté qu'une seule fois, et que les valeurs qu'il délivre sont pré-établies à ce moment là indépendamment ce qui se passe dans la boucle. Voici un exemple qui illustre ça:
create table list(x int);
insert into list values (1),(2),(3),(4);
do $$
declare i integer;
begin
for i in select x from list
loop
raise notice 'i=%', i;
update list set x=10;
i:=12;
end loop;
end
$$ language plpgsql;
Ici dans la boucle la variable de boucle est modifiée, ainsi que l'intégralité du contenu de la table sur laquelle on boucle.
Et ca n'a aucun effet sur les valeurs de i au tour suivant de la boucle, dans le sens où la sortie est :
NOTICE: i=1
NOTICE: i=2
NOTICE: i=3
NOTICE: i=4
Et si ce select avait une clause WHERE dont les valeurs changent dans la boucle, ce serait pareil. La clause WHERE ne serait pas réévaluée.
@DanielVerite
http://blog-postgresql.verite.pro/
Hors ligne
Oui c'est çà, la modification n'est pas ré évaluer dans la suite des itérations.
Voilà pourquoi jai du passer par un traçage pour faire connaitre au boucle les changements de valeurs.
Mais est ce que ce principe de traitement de boucle for avec un select est valable pour tout sgbd ?
Hors ligne
Il n'y a pas de boucles en SQL, donc les boucles FOR sont des extensions procédurales qui sont plus ou moins différentes dans chaque SGBD.
A propos du fait qu'il n'y a pas de boucle, la logique itérative dans l'exemple avec la boucle FOR interne devrait être repensée différemment.
C'est-à-dire que si on considère
FOR variable in SELECT ... FROM T2.. WHERE...
LOOP
UPDATE T2 SET.. etc....
END LOOP;
En logique SQL qui n'essaie pas de dupliquer la pensée procédurale, tout ça devrait être un seul UPDATE qui fait les mises à jour nécessaires sur les lignes concernées en une seule fois, avec comme conséquence qu'il n'y a plus de question de réévaluation ou pas des critères de la boucle FOR à chaque tour, puisqu'il n'y a plus de boucle FOR.
@DanielVerite
http://blog-postgresql.verite.pro/
Hors ligne
Il y a cela dit la notion de curseur [ INSENSITIVE ] en SQL, ainsi que la clause WHERE CURRENT OF sur les opérations DML qui permettent ce genre d'opérations en pur SQL. À noter que postgres ne supporte que les curseurs "insensitive".
Julien.
https://rjuju.github.io/
Hors ligne
Salut à tous,
Merci pour ces réponses.
dverite > C'est vrai qu'un simple UPDATE pourrait sans doute faire les mises à jour nécessaires dans cet exemple mais en réalité il existe d'autres traitement à faire qui nécessite de passer par une boucle FOR. Ici l'exemple ne le montre pas car c'est plus simplifié pour décrire le besoin.
rjuju > Interessant, pourrais tu plus expliquer et décrire le sujet stp (WHERE CURRENT OF et curseur insensitive)?
Hors ligne
https://www.postgresql.org/docs/current … clare.html
INSENSITIVE
Indicates that data retrieved from the cursor should be unaffected by updates to the table(s) underlying the cursor that occur after the cursor is created. In PostgreSQL, this is the default behavior; so this key word has no effect and is only accepted for compatibility with the SQL standard.
https://www.postgresql.org/docs/current/sql-update.html
cursor_name
The name of the cursor to use in a WHERE CURRENT OF condition. The row to be updated is the one most recently fetched from this cursor. The cursor must be a non-grouping query on the UPDATE's target table. Note that WHERE CURRENT OF cannot be specified together with a Boolean condition. See DECLARE for more information about using cursors with WHERE CURRENT OF.
Julien.
https://rjuju.github.io/
Hors ligne
rjuju> Si je comprend INSENSITIVE explique le fait que la mise à jour dans la boucle n'est pas perceptible durant la boucle (dans les prochaines itérations) ?
Et WHERE CURRENT OF cursor_name met à jour la ligne sur laquelle le curseur pointe, mais cette modification ne sera pas évaluer à la prochaine itération puisque le curseur est INSENSITIVE ?
C'est çà ?
Hors ligne
Oui, même si l'utilisation d'un curseur dans une boucle FOR ... IN SELECT est probablement un détail d'implémentation.
Julien.
https://rjuju.github.io/
Hors ligne
Merci à vous pour ces explications.
Çà serait plutôt quand même sympa si on pouvait lors d'élaboration de fonctions plpgsql d'activer ou de désactiver ce comportement INSENSITIVE de la boucle FOR.
Hors ligne
Il semble que le besoin de votre fonction soit schématiquement:
Tant qu'il y a des enregistrements qui vérifient certains critères
faire certains traitements susceptibles de modifier ces enregistrements
Et donc la condition "tant que" serait à réévaluer à chaque fois.
La boucle FOR n'est pas spécialement adaptée à ça, mais il y a d'autres types de boucle
et structures de contrôle qui peuvent être plus appropriées:
@DanielVerite
http://blog-postgresql.verite.pro/
Hors ligne