PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Percorrer as colunas de RECORD


Como o @Pavel explicou, não é simplesmente possível percorrer um registro, como você pode percorrer um array. Mas existem várias maneiras de contornar isso - dependendo de suas necessidades exatas. Por fim, como você deseja retornar todos os valores na mesma coluna, você precisa convertê-los para o mesmo tipo - text é a base comum óbvia, porque há uma representação de texto para cada tipo.

Rápido e sujo


Digamos que você tenha uma tabela com um integer , um text e uma date coluna.
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

Então a solução pode ser simples como:
SELECT unnest(string_to_array(trim(t::text, '()'), ','))
FROM   tbl t;

Funciona para as duas primeiras linhas, mas falha para os casos especiais das linhas 3 e 4.
Você pode resolver facilmente o problema com vírgulas na representação de texto:
SELECT unnest(('{' || trim(t::text, '()') || '}')::text[])
FROM   tbl t
WHERE  a < 4;

Isso funcionaria bem - exceto para a linha 4 que tem aspas duplas na representação do texto. Aqueles são escapados dobrando-os. Mas o construtor da matriz precisaria deles escapados por \ . Não sei por que essa incompatibilidade existe ...
SELECT ('{' || trim(t::text, '()') || '}') FROM tbl t WHERE a = 4

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

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

Solução adequada


Se você soubesse os nomes das colunas de antemão, uma solução limpa seria simples:
SELECT unnest(ARRAY[a::text,b::text,c::text])
FROM tbl

Como você opera em registros do tipo bem conhecido, basta consultar o catálogo do sistema:
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

Coloque isso em uma função com SQL dinâmico:
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$;

Ligar:
SELECT unnest_table('tbl') AS val

Devoluções:
val
-----
1
1text
2012-10-01
2
2text
2012-10-02
3
,3,ex,
2012-10-03
4
",4,"ex,"
2012-10-04

Isso funciona sem instalar módulos adicionais. Outra opção é instalar a extensão hstore e usá-la como @Craig demonstra.