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

Sélectionner les cellules de ligne comme nouvelles colonnes

Cette question était beaucoup plus difficile à résoudre que ce à quoi vous vous attendiez. Votre tentative avec crosstab() visait dans la bonne direction. Mais pour attribuer des noms de colonnes dynamiques, vous avez besoin de SQL dynamique en plus :EXECUTE dans une fonction plpgsql.

Changer le type de données de la colonne infos.type à partir de text à regtype pour empêcher l'injection SQL et d'autres erreurs. Par exemple, vous avez le type de données number , qui n'est pas un type de données PostgreSQL valide. Je l'ai remplacé par numeric , donc ça peut marcher.

Vous pourriez simplifiez la tâche en évitant les noms de colonne qui nécessitent des guillemets doubles. Comme nume_anterior au lieu de "nume anterior" .

Vous voudrez peut-être ajouter une colonne row_id à votre table info_data pour marquer tous les éléments d'une ligne. Vous en avez besoin pour le crosstab() fonction, et il vous permet d'ignorer les colonnes avec NULL valeurs. Le crosstab() fonction avec deux paramètres peut traiter les colonnes manquantes. Je synthétise la colonne manquante avec l'expression (d.id-1)/13 ci-dessous - qui fonctionne pour les données de votre exemple.

Vous devez installer le module supplémentaire tablefunc (une fois par base de données) :

CREATE EXTENSION tablefunc;

Trouvez des explications supplémentaires et des liens dans cette réponse associée .

Cette fonction fera ce que vous recherchez :

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

Appel :

SELECT * FROM x.mytbl();

Ne soyez pas confus par le dollar-quoting .

BTW :J'ai créé la liste des colonnes avec cette déclaration :

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