web-dev-qa-db-fra.com

Renvoie setof record (table virtuelle) de la fonction

J'ai besoin d'une fonction Postgres pour retourner une table virtuelle (comme dans Oracle) avec un contenu personnalisé. Le tableau aurait 3 colonnes et un nombre inconnu de lignes.

Je n'arrivais tout simplement pas à trouver la syntaxe correcte sur Internet.

Imagine ça:

CREATE OR REPLACE FUNCTION "public"."storeopeninghours_tostring" (numeric)
  RETURNS setof record AS
DECLARE
  open_id ALIAS FOR $1;
  returnrecords setof record;
BEGIN
  insert into returnrecords('1', '2', '3');
  insert into returnrecords('3', '4', '5');
  insert into returnrecords('3', '4', '5');
  RETURN returnrecords;
END;

Comment est-ce écrit correctement?

37
David

(Tout cela est testé avec postgresql 8.3.7 - avez-vous une version antérieure? Il suffit de regarder votre utilisation de "ALIAS FOR $ 1")

CREATE OR REPLACE FUNCTION storeopeninghours_tostring(numeric)
 RETURNS SETOF RECORD AS $$
DECLARE
 open_id ALIAS FOR $1;
 result RECORD;
BEGIN
 RETURN QUERY SELECT '1', '2', '3';
 RETURN QUERY SELECT '3', '4', '5';
 RETURN QUERY SELECT '3', '4', '5';
END
$$;

Si vous avez un enregistrement ou une variable de ligne à renvoyer (au lieu d'un résultat de requête), utilisez "RETOUR SUIVANT" plutôt que "RETOUR QUESTION".

Pour appeler la fonction, vous devez faire quelque chose comme:

select * from storeopeninghours_tostring(1) f(a text, b text, c text);

Vous devez donc définir ce que vous attendez du schéma de ligne de sortie de la fonction dans la requête. Pour éviter cela, vous pouvez spécifier des variables de sortie dans la définition de fonction:

CREATE OR REPLACE FUNCTION storeopeninghours_tostring(open_id numeric, a OUT text, b OUT text, c OUT text)
 RETURNS SETOF RECORD LANGUAGE 'plpgsql' STABLE STRICT AS $$
BEGIN
 RETURN QUERY SELECT '1'::text, '2'::text, '3'::text;
 RETURN QUERY SELECT '3'::text, '4'::text, '5'::text;
 RETURN QUERY SELECT '3'::text, '4'::text, '5'::text;
END
$$;

(je ne sais pas trop pourquoi les transtypages extra :: text sont requis ... '1' est un varchar par défaut peut-être?)

37
araqnid

Toutes les réponses déjà existantes sont obsolètes ou étaient inefficaces au départ.

En supposant que vous souhaitiez renvoyer trois colonnes integer.

Fonction PL/pgSQL

Voici comment procéder avec PL/pgSQL moderne (PostgreSQL 8.4 ou version ultérieure):

CREATE OR REPLACE FUNCTION f_foo() -- (open_id numeric) -- parameter not used
  RETURNS TABLE (a int, b int, c int) AS
$func$
BEGIN
RETURN QUERY VALUES
  (1,2,3)
, (3,4,5)
, (3,4,5)
;
END
$func$  LANGUAGE plpgsql IMMUTABLE ROWS 3;

Dans Postgres 9.6 ou version ultérieure, vous pouvez également ajouter PARALLEL SAFE .

Appel:

SELECT * FROM f_foo();

Points majeurs

  • Utilisation RETURNS TABLE pour définir un type de ligne ad hoc à renvoyer.
    Ou RETURNS SETOF mytbl pour utiliser un type de ligne prédéfini.

  • Utilisation RETURN QUERY pour renvoyer plusieurs lignes avec une seule commande.

  • Utilisez une expression VALUES pour saisir manuellement plusieurs lignes. Ceci est du SQL standard et existe depuis toujours pour toujours.

  • Si vous avez réellement besoin d'un paramètre, utilisez un nom de paramètre (open_id numeric) au lieu de ALIAS, ce qui est déconseillé . Dans l'exemple le paramètre n'a pas été utilisé et juste du bruit ...

  • Pas besoin de citer deux fois des identifiants parfaitement légaux. Les guillemets doubles sont uniquement nécessaires pour forcer les noms autrement illégaux (casse mixte, caractères illégaux ou mots réservés).

  • La volatilité des fonctions peut être IMMUTABLE , car le résultat ne change jamais.

  • ROWS 3 est facultatif, mais comme nous - savons combien de lignes sont retournées, nous pourrions aussi bien le déclarer à Postgres. Peut aider le planificateur de requêtes à choisir le meilleur plan.

SQL simple

Pour un cas simple comme celui-ci, vous pouvez utiliser une instruction SQL simple à la place:

VALUES (1,2,3), (3,4,5), (3,4,5)

Ou, si vous souhaitez (ou avez) définir des noms et des types de colonnes spécifiques:

SELECT *
FROM  (
   VALUES (1::int, 2::int, 3::int)
        , (3, 4, 5)
        , (3, 4, 5)
   ) AS t(a, b, c);

Fonction SQL

Vous pouvez l'envelopper dans un simple fonction SQL à la place:

CREATE OR REPLACE FUNCTION f_foo()
   RETURNS TABLE (a int, b int, c int) AS
$func$
   VALUES (1, 2, 3)
        , (3, 4, 5)
        , (3, 4, 5);
$func$  LANGUAGE sql IMMUTABLE ROWS 3;
37
Erwin Brandstetter

J'utilise pas mal de tables temporaires dans mes fonctions. Vous devez créer un type de retour sur la base de données, puis créer une variable de ce type à renvoyer. Voici un exemple de code qui fait exactement cela.

CREATE TYPE storeopeninghours_tostring_rs AS
(colone text,
 coltwo text,
 colthree text
);

CREATE OR REPLACE FUNCTION "public"."storeopeninghours_tostring" () RETURNS setof storeopeninghours_tostring_rs AS
$BODY$
DECLARE
  returnrec storeopeninghours_tostring_rs;
BEGIN
    BEGIN 
        CREATE TEMPORARY TABLE tmpopeninghours (
            colone text,
            coltwo text,
            colthree text
        );
    EXCEPTION WHEN OTHERS THEN
        TRUNCATE TABLE tmpopeninghours; -- TRUNCATE if the table already exists within the session.
    END;
    insert into tmpopeninghours VALUES ('1', '2', '3');
    insert into tmpopeninghours VALUES ('3', '4', '5');
    insert into tmpopeninghours VALUES ('3', '4', '5');

    FOR returnrec IN SELECT * FROM tmpopeninghours LOOP
        RETURN NEXT returnrec;
    END LOOP;
END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;


select * from storeopeninghours_tostring()
23
Imraan Parker

Pour ceux qui ont atterri ici à la recherche de l'équivalent MSSQL de création d'une table temporaire et de vidage de ses enregistrements en tant que retour ... qui n'existe pas dans PostgreSQL :( - vous devez définir le type de retour. Il y a deux façons de faire ceci, au moment de la création de la fonction ou au moment de la création de la requête.

Voir ici: http://wiki.postgresql.org/wiki/Return_more_than_one_row_of_data_from_PL/pgSQL_functions

8
Brad Holbrook
CREATE OR REPLACE FUNCTION foo(open_id numeric, OUT p1 varchar, OUT p2 varchar, OUT p3 varchar) RETURNS SETOF RECORD AS $$
BEGIN
  p1 := '1'; p2 := '2'; p3 := '3';
  RETURN NEXT; 
  p1 := '3'; p2 := '4'; p3 := '5';
  RETURN NEXT; 
  p1 := '3'; p2 := '4'; p3 := '5';
  RETURN NEXT; 
  RETURN;
END;
$$ LANGUAGE plpgsql;
7
Pavel Stehule