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;