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

Selecionar células de linha como novas colunas


Esta pergunta foi muito mais difícil para resolver do que você pode ter esperado. Sua tentativa com crosstab() apontava na direção certa. Mas para atribuir nomes de colunas dinâmicas, você precisa de SQL dinâmico adicionalmente:EXECUTE em uma função plpgsql.

Altere o tipo de dados da coluna infos.type de text para regtype para evitar injeção de SQL e outros erros. Por exemplo, você tem o tipo de dados number , que não é um tipo de dados válido do PostgreSQL. Eu o substituí por numeric , para que possa funcionar.

Você poderia simplifique a tarefa evitando nomes de colunas que precisam de aspas duplas. Como nume_anterior em vez de "nume anterior" .

Você pode querer adicionar uma coluna row_id para sua tabela info_data para marcar todos os elementos de uma linha. Você precisa dele para o crosstab() função, e permite que você ignore colunas com NULL valores. A crosstab() função com dois parâmetros pode lidar com colunas ausentes. Sintetizo a coluna ausente com a expressão (d.id-1)/13 abaixo - que funciona para os dados em seu exemplo.

Você precisa instalar o módulo adicional tablefunc (uma vez por banco de dados):
CREATE EXTENSION tablefunc;

Encontre explicações e links adicionais nesta resposta relacionada .

Esta função fará o que está procurando:
CREATE OR REPLACE FUNCTION f_mytbl()
  RETURNS TABLE (id int
, nume text           , prenume text       , cnp numeric
, "nume anterior" text, "stare civila" text, cetatenie text
, rezidenta text      , adresa text        , "tip act" text
, "serie ci" text     , "numar ci" text    , "data eliberarii" text
, "eliberat de" text)
  LANGUAGE plpgsql AS
$BODY$
BEGIN

RETURN QUERY EXECUTE $f$
SELECT *
FROM   crosstab(
    'SELECT (d.id-1)/13 -- AS row_id
          , i.id, d.value
     FROM   infos i
     JOIN   info_data d ON d.id_info = i.id
     ORDER  BY 1, i.id',

    'SELECT id
     FROM   infos
     ORDER  BY id'
    )
AS tbl ($f$ || 'id int,
, nume text           , prenume text       , cnp numeric
, "nume anterior" text, "stare civila" text, cetatenie text
, rezidenta text      , adresa text        , "tip act" text
, "serie ci" text     , "numar ci" text    , "data eliberarii" text
, "eliberat de" text)';

END;
$BODY$;

Ligar:
SELECT * FROM x.mytbl();

Não se confunda com o cotação de dólares .

BTW:eu criei a lista de colunas com esta declaração:
SELECT 'id int,' || string_agg(quote_ident(name) || ' ' || type
                              ,', ' ORDER BY i.id) 
FROM   infos i;