sql >> Base de Datos >  >> RDS >> PostgreSQL

Seleccionar celdas de fila como nuevas columnas

Esta pregunta fue mucho más difícil resolver de lo que esperaba. Tu intento con crosstab() apuntaba en la dirección correcta. Pero para asignar nombres de columnas dinámicas, necesita además SQL dinámico:EXECUTE en una función plpgsql.

Cambia el tipo de datos de la columna infos.type de text a regtype para evitar la inyección SQL y otros errores. Por ejemplo, tiene el tipo de datos number , que no es un tipo de datos de PostgreSQL válido. Lo reemplacé con numeric , para que pueda funcionar.

podrías simplifique la tarea evitando los nombres de columna que necesitan comillas dobles. Me gusta nume_anterior en lugar de "nume anterior" .

Es posible que desee agregar una columna row_id a su tabla info_data para marcar todos los elementos de una fila. Lo necesitas para el crosstab() función, y le permite ignorar las columnas con NULL valores. La crosstab() La función con dos parámetros puede ocuparse de las columnas que faltan. Sintetizo la columna que falta con la expresión (d.id-1)/13 a continuación, que funciona para los datos de su ejemplo.

Debe instalar el módulo adicional tablefunc (una vez por base de datos):

CREATE EXTENSION tablefunc;

Encuentre explicaciones y enlaces adicionales en esta respuesta relacionada .

Esta función hará lo que estás buscando:

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$;

Llamar:

SELECT * FROM x.mytbl();

No se confunda con el anidado cotización de dólares .

Por cierto:creé la lista de columnas con esta declaración:

SELECT 'id int,' || string_agg(quote_ident(name) || ' ' || type
                              ,', ' ORDER BY i.id) 
FROM   infos i;