﻿{"id":8919,"date":"2019-06-26T08:27:19","date_gmt":"2019-06-26T06:27:19","guid":{"rendered":"http:\/\/www.sigterritoires.fr\/?p=8919"},"modified":"2019-06-26T08:31:27","modified_gmt":"2019-06-26T06:31:27","slug":"postgresql-et-pl-pgsql-une-petite-initiation-aux-fonctions","status":"publish","type":"post","link":"https:\/\/www.sigterritoires.fr\/index.php\/postgresql-et-pl-pgsql-une-petite-initiation-aux-fonctions\/","title":{"rendered":"Postgresql et PL\/pgsql: une petite initiation aux fonctions"},"content":{"rendered":"\n<p>PL\/pgsql est un puissant langage de script SQL fortement influenc\u00e9 par PL\/SQL, le langage de proc\u00e9dure stock\u00e9 distribu\u00e9 avec Oracle. Il est inclus dans la grande majorit\u00e9 des installations de PostgreSQL comme une partie standard du produit, donc il ne n\u00e9cessite aucune configuration  pour \u00eatre utilis\u00e9.<br> PL\/pgsql a aussi un petit c\u00f4t\u00e9 secret . Les d\u00e9veloppeurs PostgreSQL ne veulent pas que vous sachiez que c\u2019est un langage de d\u00e9veloppement SQL \u00e0 part enti\u00e8re, capable de faire \u00e0 peu pr\u00e8s tout dans la base de donn\u00e9es PostgreSQL.<br> Pourquoi est-ce un secret ? Pendant des ann\u00e9es, PostgreSQL n\u2019a pas pr\u00e9tendu avoir des proc\u00e9dures stock\u00e9es.<br> Les fonctions PL\/pgsql ont \u00e9t\u00e9 con\u00e7ues \u00e0 l\u2019origine pour retourner des valeurs scalaires et \u00e9taient destin\u00e9es \u00e0 des t\u00e2ches math\u00e9matiques simples et \u00e0 une manipulation triviale de cha\u00eenes.<br> Au fil des ans, PL\/pgsql a d\u00e9velopp\u00e9 un ensemble riche de structures de contr\u00f4le et a acquis la capacit\u00e9 d\u2019\u00eatre utilis\u00e9 par les d\u00e9clencheurs (triggers), op\u00e9rateurs et index. En fin de compte, les d\u00e9veloppeurs ont \u00e9t\u00e9 contraints  d\u2019admettre \u00e0 contrec\u0153ur  qu\u2019ils avaient un syst\u00e8me complet de d\u00e9veloppement de proc\u00e9dures stock\u00e9es entrer leurs mains.<br> En cours de route, l\u2019objectif de PL\/pgsql a chang\u00e9 de fournir des fonctions scalaires simplistes vers la fourniture d&rsquo;une structure de contr\u00f4le compl\u00e8te. La liste compl\u00e8te de ce qui est disponible dans la version actuelle se trouve \u00e0 <a href=\"http:\/\/www.postgresql.org\/docs\/current\/static\/plpgsql-overview.html\">http:\/\/www.postgresql.org\/docs\/current\/static\/plpgsql-overview.html.<\/a><\/p>\n\n\n\n<!--more-->\n\n\n\n<p>Les fonctions sont cr\u00e9\u00e9es \u00e0 partir d&rsquo;une fen\u00eatre SQL, avec la commande CREATE FUNCTION. Elle sont ajout\u00e9es dans l&rsquo;onglet \u00ab\u00a0functions\u00a0\u00bb de votre sch\u00e9ma de base de donn\u00e9es:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/i0.wp.com\/www.sigterritoires.fr\/wp-content\/uploads\/2019\/06\/im5.png?ssl=1\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"500\" height=\"732\" data-attachment-id=\"8929\" data-permalink=\"https:\/\/www.sigterritoires.fr\/index.php\/postgresql-et-pl-pgsql-une-petite-initiation-aux-fonctions\/im5-5\/\" data-orig-file=\"https:\/\/i0.wp.com\/www.sigterritoires.fr\/wp-content\/uploads\/2019\/06\/im5.png?fit=500%2C732&amp;ssl=1\" data-orig-size=\"500,732\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"im5\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/www.sigterritoires.fr\/wp-content\/uploads\/2019\/06\/im5.png?fit=500%2C732&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/www.sigterritoires.fr\/wp-content\/uploads\/2019\/06\/im5.png?resize=500%2C732&#038;ssl=1\" alt=\"\" class=\"wp-image-8929\" srcset=\"https:\/\/i0.wp.com\/www.sigterritoires.fr\/wp-content\/uploads\/2019\/06\/im5.png?w=500&amp;ssl=1 500w, https:\/\/i0.wp.com\/www.sigterritoires.fr\/wp-content\/uploads\/2019\/06\/im5.png?resize=205%2C300&amp;ssl=1 205w\" sizes=\"auto, (max-width: 500px) 100vw, 500px\" \/><\/a><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Structure d&rsquo;une fonction PL\/pgsql<\/h2>\n\n\n\n<p>La structure d&rsquo;une fonction en PL\/pgsql est tr\u00e8s simple<\/p>\n\n\n\n<p><strong>CREATE FUNCTION <em>nom_de_la_fonction(param\u00e8tres de la fonction)<\/em><br> RETURNS   <em>r\u00e9sultat_de_la_fonction<\/em><br> AS<br> $$<br> BEGIN<br> <em>code_de_la_fonction<\/em>;<br> END<br> $$<br> LANGUAGE plpgsql;<\/strong><\/p>\n\n\n\n<p>Voyons les diff\u00e9rents \u00e9l\u00e9ments un peu plus en d\u00e9tail.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"> <strong><em>nom_de_la_fonction(param\u00e8tres de la fonction)<\/em><\/strong> <\/h3>\n\n\n\n<p>Si nous voyons ici le  nom de la fonction ET les param\u00e8tres de la fonction, c&rsquo;est parce que, en PL\/pgsql ces deux \u00e9l\u00e9ments sont indissociables. En effet, la <strong>surcharge <\/strong>(overloading) est une autre caract\u00e9ristique des fonctions PostgreSQL, qui permet plusieurs proc\u00e9dures utilisant le m\u00eame nom, mais des param\u00e8tres diff\u00e9rents. <\/p>\n\n\n\n<p>Voyons un exemple concret avec la fonction <strong>st_buffer<\/strong> incluse dans l&rsquo;extension <a href=\"https:\/\/www.sigterritoires.fr\/index.php\/debuter-avec-postgrespostgis\/\">Postgis<\/a><\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/i0.wp.com\/www.sigterritoires.fr\/wp-content\/uploads\/2019\/06\/im1.png?ssl=1\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"496\" height=\"578\" data-attachment-id=\"8920\" data-permalink=\"https:\/\/www.sigterritoires.fr\/index.php\/postgresql-et-pl-pgsql-une-petite-initiation-aux-fonctions\/im1-5\/\" data-orig-file=\"https:\/\/i0.wp.com\/www.sigterritoires.fr\/wp-content\/uploads\/2019\/06\/im1.png?fit=496%2C578&amp;ssl=1\" data-orig-size=\"496,578\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"im1\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/www.sigterritoires.fr\/wp-content\/uploads\/2019\/06\/im1.png?fit=496%2C578&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/www.sigterritoires.fr\/wp-content\/uploads\/2019\/06\/im1.png?resize=496%2C578&#038;ssl=1\" alt=\"\" class=\"wp-image-8920\" srcset=\"https:\/\/i0.wp.com\/www.sigterritoires.fr\/wp-content\/uploads\/2019\/06\/im1.png?w=496&amp;ssl=1 496w, https:\/\/i0.wp.com\/www.sigterritoires.fr\/wp-content\/uploads\/2019\/06\/im1.png?resize=257%2C300&amp;ssl=1 257w\" sizes=\"auto, (max-width: 496px) 100vw, 496px\" \/><\/a><\/figure>\n\n\n\n<p>Dans ce cas, nous avons neuf d\u00e9clarations de la fonction, chacune ayant deux ou trois param\u00e8tres de type diff\u00e9rent. Dans cet exemple, la surcharge est utilis\u00e9e pour impl\u00e9menter la construction d&rsquo;un buffer \u00e0 partir de trois types d&rsquo;entit\u00e9s diff\u00e9rentes :g\u00e9om\u00e9trie, g\u00e9ographie ou un texte contenant une g\u00e9om\u00e9trie ( le premier param\u00e8tre), un rayon (deuxi\u00e8me param\u00e8tre), et un troisi\u00e8me param\u00e8tre permettant de contr\u00f4ler le type de buffer r\u00e9sultant.<\/p>\n\n\n\n<p>Au moment de l&rsquo;appel de la fonction, Postgresql compare les types de param\u00e8tres pass\u00e9s et ex\u00e9cute la fonction correspondante. S&rsquo;il ne trouve pas de correspondance vous aurez un message du type:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/i0.wp.com\/www.sigterritoires.fr\/wp-content\/uploads\/2019\/06\/im2.png?ssl=1\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"597\" height=\"146\" data-attachment-id=\"8921\" data-permalink=\"https:\/\/www.sigterritoires.fr\/index.php\/postgresql-et-pl-pgsql-une-petite-initiation-aux-fonctions\/im2-5\/\" data-orig-file=\"https:\/\/i0.wp.com\/www.sigterritoires.fr\/wp-content\/uploads\/2019\/06\/im2.png?fit=597%2C146&amp;ssl=1\" data-orig-size=\"597,146\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"im2\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/www.sigterritoires.fr\/wp-content\/uploads\/2019\/06\/im2.png?fit=597%2C146&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/www.sigterritoires.fr\/wp-content\/uploads\/2019\/06\/im2.png?resize=597%2C146&#038;ssl=1\" alt=\"\" class=\"wp-image-8921\" srcset=\"https:\/\/i0.wp.com\/www.sigterritoires.fr\/wp-content\/uploads\/2019\/06\/im2.png?w=597&amp;ssl=1 597w, https:\/\/i0.wp.com\/www.sigterritoires.fr\/wp-content\/uploads\/2019\/06\/im2.png?resize=300%2C73&amp;ssl=1 300w\" sizes=\"auto, (max-width: 597px) 100vw, 597px\" \/><\/a><\/figure>\n\n\n\n<p>Il faut absolument d\u00e9finir le type de donn\u00e9e de chaque param\u00e8tre, par exemple<\/p>\n\n\n\n<p><strong>fct_mid(varchar, integer, integer)<\/strong><\/p>\n\n\n\n<p>Dans ce cas vous pourrez vous r\u00e9f\u00e9rer dans le code de la fonction \u00e0 chaque param\u00e8tre par son num\u00e9ro d&rsquo;ordre <\/p>\n\n\n\n<p><strong>substring($1,$2,$3)<\/strong><\/p>\n\n\n\n<p>Mais c&rsquo;est plus facile \u00e0 lire si vous donnez un nom de variable \u00e0 chaque param\u00e8tre:<\/p>\n\n\n\n<p><strong> fct_mid(<\/strong><em><strong>chaine <\/strong><\/em><strong>varchar, <\/strong><em><strong>cardeb <\/strong><\/em><strong>integer, <\/strong><em><strong>longueur <\/strong><\/em><strong>integer) <\/strong><\/p>\n\n\n\n<p>Et dans le code:<\/p>\n\n\n\n<p><strong>substring(chaine,cardeb,longueur)<\/strong><\/p>\n\n\n\n<h3 class=\"wp-block-heading\"> <strong><em>r\u00e9sultat_de_la_fonction<\/em><\/strong> <\/h3>\n\n\n\n<p>Le r\u00e9sultat le plus simple d&rsquo;une fonction est un scalaire. Vous aurez alors une ligne indiquant quel type de donn\u00e9e est retourn\u00e9e par la fonction:<\/p>\n\n\n\n<p>RETURNS varchar<\/p>\n\n\n\n<p>RETURNS integer<\/p>\n\n\n\n<p>etc&#8230;<\/p>\n\n\n\n<p>Mais il y a des retours plus complexes qui sont possibles. En premier, c&rsquo;est le retour d&rsquo;un ou plusieurs enregistrements d&rsquo;une table existante. Par exemple:<\/p>\n\n\n\n<p>CREATE OR REPLACE FUNCTION af_test()<br> <strong>RETURNS setof cercle<\/strong><br> AS $$<br> BEGIN<br> RETURN QUERY SELECT * FROM cercle WHERE id&lt;8;<br> END;<br> $$ LANGUAGE plpgsql;<\/p>\n\n\n\n<p>Cette fonction s\u00e9lectionne tous les enregistrements de la table \u00ab\u00a0cercle\u00a0\u00bb\u00a0\u00bb avec un identifiant inf\u00e9rieur \u00e0 8. Le r\u00e9sultat est:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/i0.wp.com\/www.sigterritoires.fr\/wp-content\/uploads\/2019\/06\/im3.png?ssl=1\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"353\" height=\"219\" data-attachment-id=\"8923\" data-permalink=\"https:\/\/www.sigterritoires.fr\/index.php\/postgresql-et-pl-pgsql-une-petite-initiation-aux-fonctions\/im3-5\/\" data-orig-file=\"https:\/\/i0.wp.com\/www.sigterritoires.fr\/wp-content\/uploads\/2019\/06\/im3.png?fit=353%2C219&amp;ssl=1\" data-orig-size=\"353,219\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"im3\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/www.sigterritoires.fr\/wp-content\/uploads\/2019\/06\/im3.png?fit=353%2C219&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/www.sigterritoires.fr\/wp-content\/uploads\/2019\/06\/im3.png?resize=353%2C219&#038;ssl=1\" alt=\"\" class=\"wp-image-8923\" srcset=\"https:\/\/i0.wp.com\/www.sigterritoires.fr\/wp-content\/uploads\/2019\/06\/im3.png?w=353&amp;ssl=1 353w, https:\/\/i0.wp.com\/www.sigterritoires.fr\/wp-content\/uploads\/2019\/06\/im3.png?resize=300%2C186&amp;ssl=1 300w\" sizes=\"auto, (max-width: 353px) 100vw, 353px\" \/><\/a><\/figure>\n\n\n\n<p>Ce qui indique qu&rsquo;on veut des enregistrements en sortie est le terme <strong>SETOF<\/strong>. Ici suivi par le nom de la table concern\u00e9e.<\/p>\n\n\n\n<p>Mais il se peut aussi que le r\u00e9sultat soit une s\u00e9rie d&rsquo;enregistrements calcul\u00e9s lors de l&rsquo;ex\u00e9cution de la fonction, par exemple si l&rsquo;on souhaite l&rsquo;intersection de deux couches. Le r\u00e9sultat n&rsquo;est ni l&rsquo;une ni l&rsquo;autre couche.<\/p>\n\n\n\n<p>On d\u00e9clare alors <\/p>\n\n\n\n<p><strong>RETURNS SETOF RECORD<\/strong><\/p>\n\n\n\n<p>est on ajoute une d\u00e9finition des variables qui vont faire partie de cet enregistrement. Il y a plusieurs fa\u00e7ons de faire ceci, mais la plus simple est de rajouter dans la liste des param\u00e8tres de la fonction les attributs en sortie.<\/p>\n\n\n\n<p>Ceci se fait en d\u00e9finissant les param\u00e8tres avec le mot OUT:<\/p>\n\n\n\n<p>CREATE OR REPLACE FUNCTION public.af_union(<br>     tblA character varying, <br>     idA character varying,<br>     geomA character varying,<br>     tblB character varying,<br>     idB character varying,<br>     geomB character varying,<br>    <strong> OUT id1 integer,<br>     OUT id2 integer,<br>     OUT the_geom geometry<\/strong>)<br>     RETURNS SETOF record <\/p>\n\n\n\n<p>Dans cet exemple, la fonction qui poss\u00e8de 6 param\u00e8tres en entr\u00e9e, aura comme r\u00e9sultat une s\u00e9rie d&rsquo;enregistrements avec trois attributs: id1, id2 et the_geom.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"> Codage de la fonction <\/h3>\n\n\n\n<p>Il est impossible de d\u00e9crire ici toutes les possibilit\u00e9s de codage. Voyons les principux oncepts. Le but de r\u00e9aliser une fonction est de pouvoir manipuler les requ\u00eates SQL. Le langage SQL ne permet pas de traduire un algorithme conditionnel tandis que le PL\/pgql est justement fait pour cela.<\/p>\n\n\n\n<p>Trois fonctions conditionnelles principales permettent de d\u00e9terminer les requ\u00eates SQL \u00e0 r\u00e9aliser selon le cas:<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"> la fonction CASE <\/h4>\n\n\n\n<p>De type:<\/p>\n\n\n\n<p><strong><em>CASE nom_variable<br> WHEN valeurA<br> THEN r\u00e9sultat_si_oui<br> ELSE r\u00e9sultat_si_non<br> END,<\/em><\/strong><\/p>\n\n\n\n<h4 class=\"wp-block-heading\">la fonction IF<\/h4>\n\n\n\n<p>De type:<\/p>\n\n\n\n<p><strong><em>IF(nom_variable =valeurA, r\u00e9sultat_si_oui , r\u00e9sultat_si_non)<\/em><\/strong><\/p>\n\n\n\n<h4 class=\"wp-block-heading\">La fonction LOOP<\/h4>\n\n\n\n<p>De type:<\/p>\n\n\n\n<p><strong><em>LOOP<br>Code \u00e0 ex\u00e9cuter<br>END LOOP;<\/em><\/strong><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">La substitution de variables dans les requ\u00eates<\/h2>\n\n\n\n<p>Une des utilisations les plus courantes des fonctions est de stocker des traitements et de pouvoir les ex\u00e9cuter sur des couches diff\u00e9rentes.<\/p>\n\n\n\n<p>Dans un article pr\u00e9c\u00e9dent(<a href=\"https:\/\/www.sigterritoires.fr\/index.php\/lanalyse-spatiale-avec-sql-4-fonctions-de-traitement-spatial\/\">L&rsquo;analyse spatiale avec SQL (4)<\/a>), nous avons vu un exemple de requ\u00eate pour extraire l&rsquo;intersection de deux couches appel\u00e9es <strong>cercle <\/strong>et <strong>rectangles<\/strong>:<\/p>\n\n\n\n<p><strong><em><strong>select <em>cercle.id<\/em> as id1,<em>rectangles.id<\/em> as id2, st_intersection(<em>cercle.geom<\/em>,<em>rectangles.geom<\/em>) as the_geom<\/strong><br><strong>from <em>cercle,rectangles<\/em><\/strong><br><strong>where st_intersects(<em>cercle.geom,rectangles.geom<\/em>)<\/strong><br><strong>group by <em>cercle.id,rectangles.id<\/em><\/strong><\/em><\/strong><\/p>\n\n\n\n<p>Le r\u00e9sultat est une nouvelle couche avec trois attributs: les deux identifiants des couches intersect\u00e9es et la surface d&rsquo;intersection. On peut stocker cette requ\u00eate et chaque fois que nous voulons l&rsquo;utiliser, la charger et remplacer manuellement les noms des deux couches.<\/p>\n\n\n\n<p>Mais on peut aussi stocker cette requ\u00eate sous forme d&rsquo;une fonction, que nous appellerons par exemple <em><strong>mon_intersection<\/strong><\/em>, en lui donnant comme param\u00e8tres les deux noms de tables \u00e0 intersecter. On pourra alors rentrer simplement<\/p>\n\n\n\n<p><strong>SELECT * FROM mon_intersection(&lsquo;tableA&rsquo;,&rsquo;tableB&rsquo;)<\/strong><\/p>\n\n\n\n<p>pour obtenir le m\u00eame r\u00e9sultat.<\/p>\n\n\n\n<p>Passons \u00e0 l&rsquo;\u00e9criture de la fonction:<\/p>\n\n\n\n<p><strong>CREATE OR REPLACE FUNCTION mon_intersection(<br>     tableA character varying,<br>     tableB character varying,<br>     OUT id1 integer,<br>     OUT id2 integer,<br>     OUT the_geom geometry)<br> RETURNS setof RECORD<br> AS $$<br> DECLARE<br>          rec     record;<br> BEGIN<\/strong><\/p>\n\n\n\n<p>Ici on placera le code<\/p>\n\n\n\n<p><strong>END;<br> $$ LANGUAGE plpgsql;<\/strong><\/p>\n\n\n\n<p>Nous reprenons les \u00e9l\u00e9ments vus plus haut: le retour ce sera un ensemble d&rsquo;enregistrements  nouveaux, les attributs sont d\u00e9clar\u00e9s comme param\u00e8tres OUT. Et les deux param\u00e8tres en entr\u00e9e sont les noms des deux tables \u00e0 intersecter.<\/p>\n\n\n\n<p>Le code du select que nous voulons transformer en fonction est:<\/p>\n\n\n\n<p><strong><em><strong>select <em>cercle.id<\/em> as id1,<em>rectangles.id<\/em> as id2, st_intersection(<em>cercle.geom<\/em>,<em>rectangles.geom<\/em>) as the_geom<\/strong><br><strong>from <em>cercle,rectangles<\/em><\/strong><br><strong>where st_intersects(<em>cercle.geom,rectangles.geom<\/em>)<\/strong><br><strong>group by <em>cercle.id,rectangles.id<\/em><\/strong><\/em><\/strong><\/p>\n\n\n\n<p>Nous allons tout &lsquo;abord le mettre entre guillemets pour le transformer en cha\u00eene de texte.<\/p>\n\n\n\n<p>&lsquo; <strong><em><strong>select <em>cercle.id<\/em> as id1,<em>rectangles.id<\/em> as id2, st_intersection(<em>cercle.geom<\/em>,<em>rectangles.geom<\/em>) as the_geom<\/strong><br><strong>from <em>cercle,rectangles<\/em><\/strong><br><strong>where st_intersects(<em>cercle.geom,rectangles.geom<\/em>)<\/strong><br><strong>group by <em>cercle.id,rectangles.id<\/em><\/strong><\/em><\/strong> &lsquo;<\/p>\n\n\n\n<p>Nous allons utiliser l&rsquo;op\u00e9rateur de concat\u00e9nation<strong> ||<\/strong> pour remplacer <strong>cercle <\/strong>par tableA et <strong>rectangles <\/strong>par tableB<\/p>\n\n\n\n<p><strong>&lsquo; select &lsquo;||tableA||&rsquo;.id as id1,&rsquo;||tableB||&rsquo;.id as id2, st_intersection(&lsquo;||tableA||&rsquo;.geom,&rsquo;||tableB||&rsquo;.geom) as the_geom<br> from &lsquo;||tableA||&rsquo;,&rsquo;||tableB||&rsquo;<br> where st_intersects(&lsquo;||tableA||&rsquo;.geom,&rsquo;||tableB||&rsquo;.geom)<br> group by &lsquo;||tableA||&rsquo;.id,&rsquo;||tableB||&rsquo;.id &lsquo;<\/strong><\/p>\n\n\n\n<p>Au moment de l&rsquo;ex\u00e9cution, les variables tableA et tableB  de la cha\u00eene de caract\u00e8res seront remplac\u00e9es par les noms des tables fournies en entr\u00e9e.<\/p>\n\n\n\n<p>Nous ajoutons une variable dans les d\u00e9clarations pour le texte de la requ\u00eate:<\/p>\n\n\n\n<p><strong> DECLARE<br>          rec     record;<\/strong><br>          <strong>sql      text;<\/strong><br><\/p>\n\n\n\n<p>Pour finir, on doit ajouter une boucle qui transforme chaque r\u00e9sultat de la requ\u00eate en un enregistrement en sortie<\/p>\n\n\n\n<p><strong>FOR rec IN EXECUTE sql<\/strong><\/p>\n\n\n\n<p><strong>LOOP<br>                  id1     := rec.id1;<br>                 id2     := rec.id2;<br>                 the_geom    := rec.the_geom;<br>                 RETURN NEXT;<br>  END LOOP;<\/strong><\/p>\n\n\n\n<p>Ce code indique que chaque fois que la requ\u00eate sql renvoi un r\u00e9sultat (dans la variable rec), on place les attributs contenus dans <strong>rec <\/strong>dans la file de sortie en tant que nouvel enregistrement.<\/p>\n\n\n\n<p>Et voici le code complet:<\/p>\n\n\n\n<p><strong>CREATE OR REPLACE FUNCTION mon_intersection(<br>     tableA character varying,<br>     tableB character varying,<br>     OUT id1 integer,<br>     OUT id2 integer,<br>     OUT the_geom geometry)<br> RETURNS setof RECORD<br> AS $$<br> DECLARE<br>          rec     record;<br>          sql     text;<br> BEGIN<br> sql := &lsquo; select &lsquo;||tableA||&rsquo;.id as id1,&rsquo;||tableB||&rsquo;.id as id2, st_intersection(&lsquo;||tableA||&rsquo;.geom,&rsquo;||tableB||&rsquo;.geom) as the_geom<br> from &lsquo;||tableA||&rsquo;,&rsquo;||tableB||&rsquo;<br> where st_intersects(&lsquo;||tableA||&rsquo;.geom,&rsquo;||tableB||&rsquo;.geom)<br> group by &lsquo;||tableA||&rsquo;.id,&rsquo;||tableB||&rsquo;.id &lsquo;<\/strong><\/p>\n\n\n\n<p><strong>FOR rec IN EXECUTE sql<\/strong><\/p>\n\n\n\n<p><strong>LOOP<br>                 id1     := rec.id1;<br>                 id2     := rec.id2;<br>                 the_geom    := rec.the_geom;<br>                 RETURN NEXT;<br> END LOOP;<br> END;<br> $$ LANGUAGE plpgsql;<\/strong><\/p>\n\n\n\n<p>Lors de l&rsquo;ex\u00e9cution on a le r\u00e9sultat:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/i0.wp.com\/www.sigterritoires.fr\/wp-content\/uploads\/2019\/06\/im4.png?ssl=1\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"497\" height=\"300\" data-attachment-id=\"8925\" data-permalink=\"https:\/\/www.sigterritoires.fr\/index.php\/postgresql-et-pl-pgsql-une-petite-initiation-aux-fonctions\/im4-5\/\" data-orig-file=\"https:\/\/i0.wp.com\/www.sigterritoires.fr\/wp-content\/uploads\/2019\/06\/im4.png?fit=497%2C300&amp;ssl=1\" data-orig-size=\"497,300\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"im4\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/www.sigterritoires.fr\/wp-content\/uploads\/2019\/06\/im4.png?fit=497%2C300&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/www.sigterritoires.fr\/wp-content\/uploads\/2019\/06\/im4.png?resize=497%2C300&#038;ssl=1\" alt=\"\" class=\"wp-image-8925\" srcset=\"https:\/\/i0.wp.com\/www.sigterritoires.fr\/wp-content\/uploads\/2019\/06\/im4.png?w=497&amp;ssl=1 497w, https:\/\/i0.wp.com\/www.sigterritoires.fr\/wp-content\/uploads\/2019\/06\/im4.png?resize=300%2C181&amp;ssl=1 300w\" sizes=\"auto, (max-width: 497px) 100vw, 497px\" \/><\/a><\/figure>\n\n\n\n<p>Notez qu&rsquo;il faut passer le nom des tables comme du texte, entre apostrophes, et nom comme nom de tables.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Un exemple plus complexe<\/h2>\n\n\n\n<p>Dans l&rsquo;article <a href=\"https:\/\/www.sigterritoires.fr\/index.php\/lanalyse-spatiale-avec-sql-5-fonction-dagregation-union-darcgis\/\">L\u2019analyse spatiale avec SQL: 5-fonction d\u2019agr\u00e9gation (Union d\u2019ArcGis)<\/a> nous avons vu comment \u00e9crire la requ\u00eate SQL pour r\u00e9aliser cette op\u00e9ration d&rsquo;agr\u00e9gation, pr\u00e9sente dans ArcGis et absente, pour le moment, dans Postgis.<\/p>\n\n\n\n<p>La requ\u00eate est:<\/p>\n\n\n\n<p><strong>select * <br>from<br>(select cercle.id as id1,rectangles.id as&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; id2,st_intersection(cercle.geom,rectangles.geom) as the_geom<br>from cercle,rectangles<br>where st_intersects(cercle.geom,rectangles.geom)<br>group by cercle.id,rectangles.id<br>union all<br>select 0 as id1, rectangles.id as id2,st_difference(rectangles.geom,(<br>select st_multi(st_union(the_geom) ) as the_geom from (<br>select st_intersection(cercle.geom,rectangles.geom) as the_geom<br>from cercle,rectangles<br>where st_intersects(cercle.geom,rectangles.geom) ) <br>as the_geom)) as the_geom<br>from rectangles<br>union all<br>select cercle.id as id1, 0 as id2,st_difference(cercle.geom,(<br>select st_multi(st_union(the_geom) ) as the_geom from (<br>select st_intersection(cercle.geom,rectangles.geom) as the_geom<br>from cercle,rectangles<br>where st_intersects(cercle.geom,rectangles.geom) ) <br>as the_geom)) as the_geom<br>from cercle<br>)as T1<\/strong><\/p>\n\n\n\n<p>L&rsquo;int\u00e9r\u00eat de garder ceci comme fonction param\u00e9trable est clair.<\/p>\n\n\n\n<p>Voici la fonction correspondante:<\/p>\n\n\n\n<p><strong>CREATE OR REPLACE FUNCTION public.af_union(<br>     tblA character varying, <br>     idA character varying,<br>     geomA character varying,<br>     tblB character varying,<br>     idB character varying,<br>     geomB character varying,<br>     OUT id1 integer,<br>     OUT id2 integer,<br>     OUT the_geom geometry)<br>     RETURNS SETOF record <br>     LANGUAGE &lsquo;plpgsql&rsquo;<\/strong><\/p>\n\n\n\n<p><strong>AS $BODY$<br> DECLARE<br>         sql  text;<br>         rec     record;<br> BEGIN<br> sql := &lsquo;select &lsquo;||tblA||&rsquo;.&rsquo;||idA||&rsquo; as id1,&rsquo;||tblB||&rsquo;.&rsquo;||idB||&rsquo; as id2,st_intersection(&lsquo;||tblA||&rsquo;.&rsquo;||geomA||&rsquo;,&rsquo;||tblB||&rsquo;.&rsquo;||geomB||&rsquo;) as the_geom<br> from &lsquo;||tblA||&rsquo;,&rsquo;||tblB||&rsquo;<br> where st_intersects(&lsquo;||tblA||&rsquo;.&rsquo;||geomA||&rsquo;,&rsquo;||tblB||&rsquo;.&rsquo;||geomB||&rsquo;)<br> group by &lsquo;||tblA||&rsquo;.&rsquo;||idA||&rsquo;,&rsquo;||tblB||&rsquo;.&rsquo;||idB||\u00a0\u00bb;<\/strong><\/p>\n\n\n\n<p><strong>FOR rec IN EXECUTE sql<br> LOOP<br> &#8212; Return record<br>                 id1     := rec.id1;<br>                 id2     := rec.id2;<br>                 the_geom    := rec.the_geom;<br>                 RETURN NEXT;<br>         END LOOP;<br> sql := &lsquo;select 0 as id1, &lsquo;||tblB||&rsquo;.&rsquo;||idB||&rsquo; as id2,st_difference(&lsquo;||tblB||&rsquo;.&rsquo;||geomB||&rsquo;,(<br> select st_multi(st_union(the_geom) ) as the_geom from (<br> select st_intersection(&lsquo;||tblA||&rsquo;.&rsquo;||geomA||&rsquo;,&rsquo;||tblB||&rsquo;.&rsquo;||geomB||&rsquo;) as the_geom<br> from &lsquo;||tblA||&rsquo;,&rsquo;||tblB||&rsquo;<br> where st_intersects(&lsquo;||tblA||&rsquo;.&rsquo;||geomA||&rsquo;,&rsquo;||tblB||&rsquo;.&rsquo;||geomB||&rsquo;) )<br> as the_geom)) as the_geom<br> from &lsquo;||tblB|| &lsquo; &lsquo;;<\/strong><\/p>\n\n\n\n<p><strong>FOR rec IN EXECUTE sql<br> LOOP<br> &#8212; Return record<br>                 id1     := rec.id1;<br>                 id2     := rec.id2;<br>                 the_geom    := rec.the_geom;<br>                 RETURN NEXT;<br>         END LOOP;        <\/strong><\/p>\n\n\n\n<p><strong>sql := &lsquo;select &lsquo;||tblA||&rsquo;.&rsquo;||idA||&rsquo; as id1, 0 as id2,st_difference(&lsquo;||tblA||&rsquo;.&rsquo;||geomA||&rsquo;,(<br> select st_multi(st_union(the_geom) ) as the_geom from (<br> select st_intersection(&lsquo;||tblA||&rsquo;.&rsquo;||geomA||&rsquo;,&rsquo;||tblB||&rsquo;.&rsquo;||geomB||&rsquo;) as the_geom<br> from &lsquo;||tblA||&rsquo;,&rsquo;||tblB||&rsquo;<br> where st_intersects(&lsquo;||tblA||&rsquo;.&rsquo;||geomA||&rsquo;,&rsquo;||tblB||&rsquo;.&rsquo;||geomB||&rsquo;) )<br> as the_geom)) as the_geom<br> from &lsquo;||tblA||&rsquo; &lsquo;;<br> FOR rec IN EXECUTE sql<br> LOOP<br> &#8212; Return record<br>                 id1     := rec.id1;<br>                 id2     := rec.id2;<br>                 the_geom    := rec.the_geom;<br>                 RETURN NEXT;<br>         END LOOP;<\/strong><br><strong>RETURN;<\/strong><\/p>\n\n\n\n<p><strong>END;<br> $BODY$;<\/strong><\/p>\n\n\n\n<p>Par rapport \u00e0 l&rsquo;exemple pr\u00e9c\u00e9dent, nous avons pour chaque table en entr\u00e9e ajout\u00e9 la possibilit\u00e9 d&rsquo;indiquer le nom de l&rsquo;attribut identifiant et du champ g\u00e9om\u00e9trie. L&rsquo;op\u00e9rateur UNION ALL est remplac\u00e9 ici par l&rsquo;ex\u00e9cution s\u00e9quentielle des 3 boucles FOR qui remplissent la table en sortie.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>      <\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>PL\/pgsql est un puissant langage de script SQL fortement influenc\u00e9 par PL\/SQL, le langage de proc\u00e9dure stock\u00e9 distribu\u00e9 avec Oracle. Il est inclus dans la grande majorit\u00e9 des installations de PostgreSQL comme une partie standard du&hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"give_campaign_id":0,"_bbp_topic_count":0,"_bbp_reply_count":0,"_bbp_total_topic_count":0,"_bbp_total_reply_count":0,"_bbp_voice_count":0,"_bbp_anonymous_reply_count":0,"_bbp_topic_count_hidden":0,"_bbp_reply_count_hidden":0,"_bbp_forum_subforum_count":0,"sfsi_plus_gutenberg_text_before_share":"","sfsi_plus_gutenberg_show_text_before_share":"","sfsi_plus_gutenberg_icon_type":"","sfsi_plus_gutenberg_icon_alignemt":"","sfsi_plus_gutenburg_max_per_row":"","_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_post_was_ever_published":false},"categories":[1379,311],"tags":[],"class_list":["post-8919","post","type-post","status-publish","format-standard","hentry","category-database","category-postgresql"],"aioseo_notices":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p6XU0A-2jR","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.sigterritoires.fr\/index.php\/wp-json\/wp\/v2\/posts\/8919","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.sigterritoires.fr\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sigterritoires.fr\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sigterritoires.fr\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sigterritoires.fr\/index.php\/wp-json\/wp\/v2\/comments?post=8919"}],"version-history":[{"count":0,"href":"https:\/\/www.sigterritoires.fr\/index.php\/wp-json\/wp\/v2\/posts\/8919\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.sigterritoires.fr\/index.php\/wp-json\/wp\/v2\/media?parent=8919"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sigterritoires.fr\/index.php\/wp-json\/wp\/v2\/categories?post=8919"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sigterritoires.fr\/index.php\/wp-json\/wp\/v2\/tags?post=8919"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}