Précédent   Forum des professionnels en informatique > Bases de données > PostgreSQL > Requêtes
Requêtes Forum d'entraide sur les requêtes SQL spécifiques à PostgreSQL, les triggers, les vues, etc.
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse Proposer ce sujet en actualité
 
Outils de la discussion
Publicité
'
Vieux 22/09/2011, 11h46   #1
Modérateur
 
Avatar de ymoreau
 
Homme Yoann Moreau
Ingénieur en laboratoire de recherche
Inscription : septembre 2005
Messages : 723
Détails du profil
Informations personnelles :
Nom : Homme Yoann Moreau
Âge : 26
Localisation : France, Vaucluse (Provence Alpes Côte d'Azur)

Informations professionnelles :
Activité : Ingénieur en laboratoire de recherche
Secteur : Enseignement

Informations forums :
Inscription : septembre 2005
Messages : 723
Points : 1 128
Points : 1 128
Par défaut PgSQL - Renvoyer un type composite

Bonjour,
Je travaille sur une procédure devant renvoyer plusieurs éléments contenant 2 valeurs. Je pensais donc naïvement renvoyer des paires de variables (récupérées dans la procédure) en spécifiant un type de retour que j'ai créé avec CREATE TYPE pour la sortie de la fonction.

Mais je n'ai pas trouvé si c'était faisable, et en cherchant j'ai lu une solution alternative qui serait de créer une table temporaire et de la remplir. Est-ce que mon idée de départ est réalisable, et si oui, est-elle meilleure ou moins moins bonne que l'autre ?
Sachant qu'il s'agit de récupérer et construire des résultats, donc potentiellement avoir plusieurs appels concurrents à cette procédure.

Merci d'avance.
ymoreau est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 22/09/2011, 13h36   #2
Modérateur
 
Inscription : octobre 2008
Messages : 1 508
Détails du profil
Informations personnelles :
Localisation : France, Paris (Île de France)

Informations forums :
Inscription : octobre 2008
Messages : 1 508
Points : 2 040
Points : 2 040
En plpgsql, on peut faire RETURNS type avec un type composite ou bien RETURNS SETOF type s'il s'agit de retourner plusieurs lignes de ce type composite.
C'est sans problème.
estofilo est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 22/09/2011, 14h15   #3
Modérateur
 
Avatar de ymoreau
 
Homme Yoann Moreau
Ingénieur en laboratoire de recherche
Inscription : septembre 2005
Messages : 723
Détails du profil
Informations personnelles :
Nom : Homme Yoann Moreau
Âge : 26
Localisation : France, Vaucluse (Provence Alpes Côte d'Azur)

Informations professionnelles :
Activité : Ingénieur en laboratoire de recherche
Secteur : Enseignement

Informations forums :
Inscription : septembre 2005
Messages : 723
Points : 1 128
Points : 1 128
En fait ma question était plutôt comment on renvoie effectivement les valeurs, comment on construit une ligne du type composé. D'après la doc il faut renvoyer une variable de type RECORD ou ROW, mais la ligne que je veux renvoyer est composée de plusieurs variables et je ne vois pas comment les regrouper.
ymoreau est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 22/09/2011, 14h54   #4
Modérateur
 
Inscription : octobre 2008
Messages : 1 508
Détails du profil
Informations personnelles :
Localisation : France, Paris (Île de France)

Informations forums :
Inscription : octobre 2008
Messages : 1 508
Points : 2 040
Points : 2 040
Tu peux retourner une variable du type composé en question après avoir rempli les champs un par un.
Par exemple:
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 
CREATE TYPE complex AS (
    r       double precision,
    i       double precision
);
 
CREATE OR REPLACE FUNCTION t() returns complex AS 
$$
declare
 c complex;
begin
  c.r:=2.0;
  c.i:=-2.5;
  RETURN c;
end
$$ LANGUAGE plpgsql;
 
test=> SELECT * FROM t();
 r |  i   
---+------
 2 | -2.5
estofilo est déconnecté   Envoyer un message privé Réponse avec citation 10
Vieux 22/09/2011, 15h09   #5
Modérateur
 
Avatar de ymoreau
 
Homme Yoann Moreau
Ingénieur en laboratoire de recherche
Inscription : septembre 2005
Messages : 723
Détails du profil
Informations personnelles :
Nom : Homme Yoann Moreau
Âge : 26
Localisation : France, Vaucluse (Provence Alpes Côte d'Azur)

Informations professionnelles :
Activité : Ingénieur en laboratoire de recherche
Secteur : Enseignement

Informations forums :
Inscription : septembre 2005
Messages : 723
Points : 1 128
Points : 1 128
Merci de cette réponse claire !
ymoreau est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 26/09/2011, 16h54   #6
Modérateur
 
Avatar de ymoreau
 
Homme Yoann Moreau
Ingénieur en laboratoire de recherche
Inscription : septembre 2005
Messages : 723
Détails du profil
Informations personnelles :
Nom : Homme Yoann Moreau
Âge : 26
Localisation : France, Vaucluse (Provence Alpes Côte d'Azur)

Informations professionnelles :
Activité : Ingénieur en laboratoire de recherche
Secteur : Enseignement

Informations forums :
Inscription : septembre 2005
Messages : 723
Points : 1 128
Points : 1 128
En fait je suis confronté à un autre problème pour renvoyer mon type créé, en sortie j'obtiens une seule colonne contenant mes deux champs rassemblés en une chaîne (champ1,champ2), où la colonne est le nom de ma fonction. Aussi bien en sortie dans psql qu'au travers d'une lib (ruby).

J'ai essayé de renvoyer des lignes d'une vraie table et pas d'un type créé, c'est la même chose. C'est très embêtant pour travailler sur le résultat, et je m'étonne de ce comportement par défaut. Comment faut il s'y prendre pour récupérer une sortie semblable à une requête SQL classique ?

Merci d'avance.

Au cas où voici la fonction :
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
DROP TYPE IF EXISTS nearest_tags CASCADE;
CREATE TYPE nearest_tags AS (id_doc INT, id_tag BIGINT);
 
CREATE OR REPLACE FUNCTION get_nearest_tags(word VARCHAR) RETURNS SETOF nearest_tags AS $$
DECLARE
    nearest_tag_values nearest_tags;
    idtoken integer;
    token_row inverted_index%rowtype;
    position integer;
BEGIN
    SELECT t.id_token INTO idtoken FROM tokens AS t WHERE word = t.token;
    IF NOT FOUND THEN
        RAISE EXCEPTION 'Word "%" not found', word;
    END IF;
 
    -- For each row in the inverted index, i.e. for each document containing the word
    FOR token_row IN SELECT * FROM inverted_index AS ii WHERE ii.id_token = idtoken LOOP
        nearest_tag_values.id_doc := token_row.id_doc;
 
        -- For each position of the word
        FOR position IN SELECT unnest(token_row.positions) LOOP
            SELECT t.id_tag INTO nearest_tag_values.id_tag
              FROM (SELECT id_tag, parent_tag FROM tags WHERE id_doc = token_row.id_doc AND
                    starting_offset < position AND ending_offset > position) AS t
              WHERE t.id_tag NOT IN 
                  (SELECT parent_tag FROM tags WHERE id_doc = token_row.id_doc AND
                   parent_tag IS NOT NULL AND starting_offset < position AND ending_offset > position)
              ;
            RETURN NEXT nearest_tag_values;
        END LOOP;
 
    END LOOP;
    RETURN;
END
$$ LANGUAGE 'plpgsql';
ymoreau est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 26/09/2011, 17h06   #7
ced
Rédacteur/Modérateur

 
Avatar de ced
 
Homme Cédric Duprez
Inscription : avril 2002
Messages : 3 823
Détails du profil
Informations personnelles :
Nom : Homme Cédric Duprez
Âge : 36
Localisation : France, Loiret (Centre)

Informations professionnelles :
Secteur : Agroalimentaire - Agriculture

Informations forums :
Inscription : avril 2002
Messages : 3 823
Points : 6 433
Points : 6 433
Depuis PostgreSQL 8.4 (de mémoire), les fonctions peuvent retourner un type TABLE. Plus besoin de passer par les types...
Du coup, en sortie, on récupère une sortie semblable à une requête classique.

Le lien vers la doc : http://docs.postgresql.fr/8.4/sql-createfunction.html
Il y a un exemple en bas de page, pour une fonction en SQL, mais c'est pareil en PL/PGSQL. Il suffit d'exécuter un SELECT en fin de fonction...

Voici un autre exemple :
Code :
1
2
3
4
5
6
7
8
CREATE OR REPLACE FUNCTION toto(a int)
RETURNS TABLE(b int, c int) AS $$
BEGIN
  RETURN QUERY SELECT i, i+1 FROM generate_series(1, a) AS g(i);
END;
$$ LANGUAGE plpgsql;
 
SELECT * FROM toto(10);
__________________
Rédacteur / Modérateur SGBD
Mes tutoriels et la FAQ MySQL

----------------------------------------------------
Pensez aux balises code et au tag
Je ne réponds pas aux questions techniques par message privé, les forums sont là pour ça
ced est déconnecté   Envoyer un message privé Réponse avec citation 10
Vieux 26/09/2011, 17h49   #8
Modérateur
 
Avatar de ymoreau
 
Homme Yoann Moreau
Ingénieur en laboratoire de recherche
Inscription : septembre 2005
Messages : 723
Détails du profil
Informations personnelles :
Nom : Homme Yoann Moreau
Âge : 26
Localisation : France, Vaucluse (Provence Alpes Côte d'Azur)

Informations professionnelles :
Activité : Ingénieur en laboratoire de recherche
Secteur : Enseignement

Informations forums :
Inscription : septembre 2005
Messages : 723
Points : 1 128
Points : 1 128
Merci de ta réponse, en me renseignant sur RETURNS TABLE j'ai lu que cela retournait aussi des résultats concaténés, et que la forme du résultat dépend de la manière d'exécuter la fonction. Jusque là je faisais SELECT ma_fonction();, mais pour obtenir les résultats avec des colonnes distinctes et nommées il faut faire SELECT * FROM ma_fonction();.

Du coup je n'ai pas besoin d'utiliser RETURNS TABLE. Mais ça me semble plus concis, car ça évite de créer un type. Par contre dans les exemples, il n'y a qu'un RETURN sur une requête, dans mon cas je construis mes résultats au fur et à mesure, il n'est pas possible d'en obtenir l'ensemble en une seule ligne. Est-ce qu'il est possible de renvoyer comme avec SETOF les valeurs ligne par ligne ? Je n'ai pas trouvé d'indication dans la doc ou sur le net.
ymoreau est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 27/09/2011, 10h00   #9
ced
Rédacteur/Modérateur

 
Avatar de ced
 
Homme Cédric Duprez
Inscription : avril 2002
Messages : 3 823
Détails du profil
Informations personnelles :
Nom : Homme Cédric Duprez
Âge : 36
Localisation : France, Loiret (Centre)

Informations professionnelles :
Secteur : Agroalimentaire - Agriculture

Informations forums :
Inscription : avril 2002
Messages : 3 823
Points : 6 433
Points : 6 433
Habituellement, quand j'ai besoin de faire ce genre de choses, je crée une table temporaire que je remplis au fur et à mesure de l'exécution de la fonction, puis je fais un SELECT final qui renvoie tout ce dont j'ai besoin.

Je pense que si tu fais plusieurs SELECT successifs, tu n'auras que les résultats de dernier SELECT (mais je n'ai jamais testé).
__________________
Rédacteur / Modérateur SGBD
Mes tutoriels et la FAQ MySQL

----------------------------------------------------
Pensez aux balises code et au tag
Je ne réponds pas aux questions techniques par message privé, les forums sont là pour ça
ced est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 27/09/2011, 11h55   #10
Modérateur
 
Inscription : octobre 2008
Messages : 1 508
Détails du profil
Informations personnelles :
Localisation : France, Paris (Île de France)

Informations forums :
Inscription : octobre 2008
Messages : 1 508
Points : 2 040
Points : 2 040
Citation:
Envoyé par YoniBlond Voir le message
Par contre dans les exemples, il n'y a qu'un RETURN sur une requête, dans mon cas je construis mes résultats au fur et à mesure, il n'est pas possible d'en obtenir l'ensemble en une seule ligne. Est-ce qu'il est possible de renvoyer comme avec SETOF les valeurs ligne par ligne ?
Oui c'est faisable en procédural, il faut faire RETURN NEXT sans argument sachant que les noms de colonnes de la table sont accessibles en noms de variables (équivalents implicites de variables OUT).

Par exemple:
Code :
1
2
3
4
5
6
7
8
9
10
11
CREATE OR REPLACE FUNCTION f() returns TABLE(i double precision, r double precision) AS 
$$
begin
  r:=2.0;
  i:=-2.5;
  RETURN next;
  r:=3.0;
  i:=-4.5;
  RETURN next;
end
$$ LANGUAGE plpgsql;
Résultat:
test=> select * from f();
  i   | r 
------+---
 -2.5 | 2
 -4.5 | 3
estofilo est déconnecté   Envoyer un message privé Réponse avec citation 20
Vieux 27/09/2011, 16h23   #11
Modérateur
 
Avatar de ymoreau
 
Homme Yoann Moreau
Ingénieur en laboratoire de recherche
Inscription : septembre 2005
Messages : 723
Détails du profil
Informations personnelles :
Nom : Homme Yoann Moreau
Âge : 26
Localisation : France, Vaucluse (Provence Alpes Côte d'Azur)

Informations professionnelles :
Activité : Ingénieur en laboratoire de recherche
Secteur : Enseignement

Informations forums :
Inscription : septembre 2005
Messages : 723
Points : 1 128
Points : 1 128
Merci, ça marche parfaitement
ymoreau est déconnecté   Envoyer un message privé Réponse avec citation 00
Réponse Proposer ce sujet en actualité Cette discussion est résolue.
Outils de la discussion



Fuseau horaire GMT +2. Il est actuellement 17h38.


 
 
 
 
Partenaires

Hébergement Web