PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Boucle dans les colonnes de RECORD

Comme @Pavel l'a expliqué, il n'est pas simplement possible de parcourir un enregistrement, comme vous pourriez parcourir un tableau. Mais il existe plusieurs façons de contourner cela - en fonction de vos besoins exacts. En fin de compte, puisque vous souhaitez renvoyer toutes les valeurs dans la même colonne, vous devez les convertir dans le même type - text est le terrain d'entente évident, car il existe une représentation textuelle pour chaque type.

Rapide et sale

Supposons que vous ayez une table avec un integer , un text et une date colonne.

CREATE TEMP TABLE tbl(a int, b text, c date);
INSERT INTO tbl VALUES
 (1, '1text',     '2012-10-01')
,(2, '2text',     '2012-10-02')
,(3, ',3,ex,',    '2012-10-03')  -- text with commas
,(4, '",4,"ex,"', '2012-10-04')  -- text with commas and double quotes

Alors la solution peut être aussi simple que :

SELECT unnest(string_to_array(trim(t::text, '()'), ','))
FROM   tbl t;

Fonctionne pour les deux premières lignes, mais échoue pour les cas particuliers des lignes 3 et 4.
Vous pouvez facilement résoudre le problème avec des virgules dans la représentation textuelle :

SELECT unnest(('{' || trim(t::text, '()') || '}')::text[])
FROM   tbl t
WHERE  a < 4;

Cela fonctionnerait bien - sauf pour la ligne 4 qui a des guillemets doubles dans la représentation du texte. Ceux-ci sont échappés en les doublant. Mais le constructeur de tableau aurait besoin qu'ils soient échappés par \ . Je ne sais pas pourquoi cette incompatibilité est là...

SELECT ('{' || trim(t::text, '()') || '}') FROM tbl t WHERE a = 4

Rendement :

{4,""",4,""ex,""",2012-10-04}

Mais vous auriez besoin :

SELECT '{4,"\",4,\"ex,\"",2012-10-04}'::text[];  -- works

Solution appropriée

Si vous connaissiez les noms des colonnes à l'avance, une solution propre serait simple :

SELECT unnest(ARRAY[a::text,b::text,c::text])
FROM tbl

Étant donné que vous travaillez sur des enregistrements de type bien connu, vous pouvez simplement interroger le catalogue système :

SELECT string_agg(a.attname || '::text', ',' ORDER  BY a.attnum)
FROM   pg_catalog.pg_attribute a 
WHERE  a.attrelid = 'tbl'::regclass
AND    a.attnum > 0
AND    a.attisdropped = FALSE

Mettez ceci dans une fonction avec SQL dynamique :

CREATE OR REPLACE FUNCTION unnest_table(_tbl text)
  RETURNS SETOF text LANGUAGE plpgsql AS
$func$
BEGIN

RETURN QUERY EXECUTE '
SELECT unnest(ARRAY[' || (
    SELECT string_agg(a.attname || '::text', ',' ORDER  BY a.attnum)
    FROM   pg_catalog.pg_attribute a 
    WHERE  a.attrelid = _tbl::regclass
    AND    a.attnum > 0
    AND    a.attisdropped = false
    ) || '])
FROM   ' || _tbl::regclass;

END
$func$;

Appel :

SELECT unnest_table('tbl') AS val

Renvoie :

val
-----
1
1text
2012-10-01
2
2text
2012-10-02
3
,3,ex,
2012-10-03
4
",4,"ex,"
2012-10-04

Cela fonctionne sans installer de modules supplémentaires. Une autre option consiste à installer l'extension hstore et à l'utiliser comme le montre @Craig.