Vous n'êtes pas identifié(e).

#1 07/06/2011 18:12:06

mougoye
Membre

Curseur : Eviter de boucler 2x une même requête / curseurs imbriqués

Bonjour,

Désolé d'avance si le post n'est pas clair car j'ai encore un peu de mal avec les termes débutant dans ce langage.
J'ai une fonction aaa_test qui permet d'écrire dans une table.
Dans cette fonction se trouve 3 curseurs imbriqués (2 dans 1) : sql_cursor_2 et sql_cursor_3 imbriqués dans sql_cursor.
Première question : est-il possible d'imbriqué plusieurs curseurs sans utilisé FOR .... IN EXECUTE ..... pour les curseurs suivants du style:

OPEN sql_cursor;
 
LOOP
 
	FETCH sql_cursor INTO sPol, sPlante;
 
	EXIT WHEN NOT FOUND;
 
	RAISE NOTICE 'ANALYSE % (%) ', sPol, sPlante;

		OPEN sql_cursor_2;
	 
		LOOP
			FETCH sql_cursor_2 INTO sPintersect, sArea;
             
                        EXIT WHEN NOT FOUND;

			RAISE NOTICE 'polygone : % surface : % ', sPintersect, sArea;
			
		END LOOP;

END LOOP;

Le souci c'est que le EXIT WHEN NOT FOUND nous faire sortir de tous les curseurs

Pour contourner ce problème, j'ai donc insérer deux curseurs sql_cursor_2 et sql_cursor_3
Cependant,
Deuxième question : les curseurs sql_cursor_2 et sql_cursor_3 effectuent sensiblement la même chose mise à part que le select s'effectue sur un champ différent. N'y a-t-il pas moyen d'éviter ce double "balayement" en récupérant directement les valeurs des deux champs et en les stockant dans des variables ?

Fonction entière :

-- Function: aaa_test(numeric, numeric)

-- DROP FUNCTION aaa_test(numeric, numeric);

CREATE OR REPLACE FUNCTION aaa_test(ibord numeric, iprecision numeric)
  RETURNS void AS
$BODY$
 
DECLARE 
        bVal BOOLEAN;
	sPol text;
	sPintersect text;
        sArea text;
	sPlante text;
	sSchema text;
	sTable text;
	sChamp text;
	sSrid text;
        sql_cursor cursor FOR SELECT DISTINCT(bord_carto_id),nom FROM conso_geographique, entite_carto, conso_element WHERE r_bord = iBord AND conso_geographique.rc_code = 'UTM_2-1' AND conso_geographique.ent_id = entite_carto.ent_id AND id_element = id_element_observe ORDER BY nom;
	sql_cursor_2 refcursor;
	sql_cursor_3 refcursor;
        nb_ligne integer;
 
BEGIN
 
EXECUTE 'DELETE FROM aaa_cas_test;';
 
OPEN sql_cursor;
 
LOOP
 
	FETCH sql_cursor INTO sPol, sPlante;
 
	EXIT WHEN NOT FOUND;
 
	RAISE NOTICE 'ANALYSE % (%) ', sPol, sPlante;

		FOR sql_cursor_2 IN EXECUTE 

			'SELECT ent_libelle
			FROM (SELECT max(
				area2d(
					intersection(
						ST_SnapToGrid(
							(SELECT bd.geometry FROM bord_carto AS bd WHERE bord_id = ' || sPol || '),' || iPrecision || ') , ST_SnapToGrid(geometry,' || iPrecision || '))
			)
			) AS area , 
			ent_libelle 
			FROM entite_carto AS ent 
			JOIN ref_carto AS ref ON ref.rc_id = ent.rc_id WHERE ref.rc_code =''UTM_2-1'' 
			AND INTERSECTS((SELECT bd.geometry FROM bord_carto AS bd WHERE bord_id = ' || sPol || '), geometry) 
			GROUP BY ent_id, ent_libelle ORDER BY area DESC) as rqt
			WHERE area > 0;'
	 
		LOOP
	 
			sPintersect := sql_cursor_2;

		END LOOP;	

		FOR sql_cursor_3 IN EXECUTE 

			'SELECT area 
			FROM (SELECT max(
				area2d(
					intersection(
						ST_SnapToGrid(
							(SELECT bd.geometry FROM bord_carto AS bd WHERE bord_id = ' || sPol || '),' || iPrecision || ') , ST_SnapToGrid(geometry,' || iPrecision || '))
			)
			) AS area , 
			ent_libelle 
			FROM entite_carto AS ent 
			JOIN ref_carto AS ref ON ref.rc_id = ent.rc_id WHERE ref.rc_code =''UTM_2-1'' 
			AND INTERSECTS((SELECT bd.geometry FROM bord_carto AS bd WHERE bord_id = ' || sPol || '), geometry) 
			GROUP BY ent_id, ent_libelle ORDER BY area DESC) as rqt
			WHERE area > 0;'
	 
		LOOP
			sArea := sql_cursor_3;
		END LOOP;

		RAISE NOTICE 'polygone : % surface : % ', sPintersect, sArea;

		EXECUTE 'INSERT INTO aaa_cas_test VALUES (''' || sPol || ''',''' || sPlante || ''', ''' || sPintersect || ''',''' || sArea || ''');';
 
	 	
END LOOP;
 
CLOSE sql_cursor;
RETURN;
END;

Merci par avance pour vos réponses

Hors ligne

#2 07/06/2011 18:24:45

Marc Cousin
Membre

Re : Curseur : Eviter de boucler 2x une même requête / curseurs imbriqués

Pour la première question: rien ne vous oblige à faire EXIT WHEN NOT FOUND simple.

Voici un exemple tiré de la doc:

<<ablock>>
BEGIN
    -- some computations
    IF stocks > 100000 THEN
        EXIT ablock;  -- causes exit from the BEGIN block
    END IF;
    -- computations here will be skipped when stocks > 100000
END;

Rien ne vous empêche donc de mettre des labels à chacune de vos boucles, et de faire un
EXIT  mon_label WHEN NOT FOUND;

Pour le second point, bien sûr, vous pouvez récupérer plusieurs valeurs dans un enregistrement en même temps. Tout est documenté ici: http://doc.postgresql.fr/9.0/plpgsql-cursors.html
Pour résumer, lors du fetch, donnez lui la liste des variables à affecter séparées par des virgules, au lieu d'une seule variable.


Marc.

Hors ligne

#3 08/06/2011 09:16:51

mougoye
Membre

Re : Curseur : Eviter de boucler 2x une même requête / curseurs imbriqués

Merci pour votre réponse
Je vais tester cela

Hors ligne

#4 08/06/2011 11:25:43

mougoye
Membre

Re : Curseur : Eviter de boucler 2x une même requête / curseurs imbriqués

Merci Marc, j'ai trouvé la solution grâce à votre post.
Ci-dessous la solution :

CREATE OR REPLACE FUNCTION bbb_test(ibord numeric, iprecision numeric)
  RETURNS void AS
$BODY$
 
DECLARE 
        bVal BOOLEAN;
	sPol bigint;
	sPintersect text;
        sArea double precision;
	sPlante text;
	sSchema text;
	sTable text;
	sChamp text;
	sSrid text;
        sql_cursor cursor FOR SELECT DISTINCT(bord_carto_id),nom FROM conso_geographique, entite_carto, conso_element WHERE r_bord = iBord AND conso_geographique.rc_code = 'UTM_2-1' AND conso_geographique.ent_id = entite_carto.ent_id AND id_element = id_element_observe ORDER BY nom;
	sql_cursor_2 cursor FOR SELECT ent_libelle, area
			FROM (SELECT max(
				area2d(
					intersection(
						ST_SnapToGrid(
							(SELECT bd.geometry FROM bord_carto AS bd WHERE bord_id = sPol ), iPrecision ) , ST_SnapToGrid(geometry, iPrecision ))
			)
			) AS area , 
			ent_libelle 
			FROM entite_carto AS ent 
			JOIN ref_carto AS ref ON ref.rc_id = ent.rc_id WHERE ref.rc_code ='UTM_2-1' 
			AND INTERSECTS((SELECT bd.geometry FROM bord_carto AS bd WHERE bord_id = sPol ), geometry) 
			GROUP BY ent_id, ent_libelle ORDER BY area DESC) AS rqt
			WHERE area > 0;
	--sql_cursor_3 refcursor;
        nb_ligne integer;
 
BEGIN
 
EXECUTE 'DELETE FROM aaa_cas_test;';
 
OPEN sql_cursor;
 
<<label1>>
 
LOOP
 
	FETCH sql_cursor INTO sPol, sPlante;
 
	EXIT label1 WHEN NOT FOUND;
 
	RAISE NOTICE 'ANALYSE % (%) ', sPol, sPlante;
 
 
		OPEN sql_cursor_2;
 
		<<label2>>
 
		LOOP
			FETCH sql_cursor_2 INTO sPintersect, sArea;
 
			EXIT label2 WHEN NOT FOUND;
 
			RAISE NOTICE 'polygone : % surface : % ', sPintersect, sArea;
 
			EXECUTE 'INSERT INTO aaa_cas_test VALUES (''' || sPol || ''',''' || sPlante || ''', ''' || sPintersect || ''',''' || sArea || ''');';
 
		END LOOP;
 
		CLOSE sql_cursor_2;
 
 
END LOOP;
 
CLOSE sql_cursor;
RETURN;
END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION bbb_test(numeric, numeric) OWNER TO postgres;

Hors ligne

Pied de page des forums