Vous pouvez le faire avec crosstab()
depuis le module complémentaire tablefunc :
SELECT b
, COALESCE(a1, 0) AS "A1"
, COALESCE(a2, 0) AS "A2"
, COALESCE(a3, 0) AS "A3"
, ... -- all the way up to "A30"
FROM crosstab(
'SELECT colb, cola, 1 AS val FROM matrix
ORDER BY 1,2'
, $$SELECT 'A'::text || g FROM generate_series(1,30) g$$
) AS t (b text
, a1 int, a2 int, a3 int, a4 int, a5 int, a6 int
, a7 int, a8 int, a9 int, a10 int, a11 int, a12 int
, a13 int, a14 int, a15 int, a16 int, a17 int, a18 int
, a19 int, a20 int, a21 int, a22 int, a23 int, a24 int
, a25 int, a26 int, a27 int, a28 int, a29 int, a30 int);
Si NULL
au lieu de 0
ça marche aussi, ça peut être juste SELECT *
dans la requête externe.
Explication détaillée :
- Requête croisée PostgreSQL
La « difficulté » particulière ici :aucune « valeur » réelle. Ajoutez donc 1 AS val
comme dernière colonne.
Nombre de catégories inconnu
Une requête complètement dynamique (avec un type de résultat inconnu) n'est pas possible dans une seule requête. Il vous en faut deux requêtes. Construisez d'abord dynamiquement une instruction comme ci-dessus, puis exécutez-la. Détails :
-
Sélection de plusieurs valeurs max() à l'aide d'une seule instruction SQL
-
PostgreSQL convertit les colonnes en lignes ? Transposer ?
-
Générer dynamiquement des colonnes pour le tableau croisé dans PostgreSQL
-
Alternative dynamique au pivot avec CASE et GROUP BY
Trop de catégories
Si vous dépassez le nombre maximum de colonnes (1600), un tableau croisé classique est impossible, car le résultat ne peut pas être représenté avec des colonnes individuelles. (De plus, les yeux humains seraient à peine capables de lire un tableau avec autant de colonnes)
Tableaux ou types de documents comme hstore
ou jsonb
sont l'alternative. Voici une solution avec des tableaux :
SELECT colb, array_agg(cola) AS colas
FROM (
SELECT colb, right(colb, -1)::int AS sortb
, CASE WHEN m.cola IS NULL THEN 0 ELSE 1 END AS cola
FROM (SELECT DISTINCT colb FROM matrix) b
CROSS JOIN (SELECT DISTINCT cola FROM matrix) a
LEFT JOIN matrix m USING (colb, cola)
ORDER BY sortb, right(cola, -1)::int
) sub
GROUP BY 1, sortb
ORDER BY sortb;
-
Construisez la grille de valeurs complète avec :
(SELECT DISTINCT colb FROM matrix) b CROSS JOIN (SELECT DISTINCT cola FROM matrix) a
-
LEFT JOIN
combinaisons existantes, triez par la partie numérique du nom et regroupez-les dans des tableaux.right(colb, -1)::int
supprime le premier caractère de 'A3' et convertit les chiffres en nombre entier afin d'obtenir un ordre de tri approprié.
Matrice de base
Si vous voulez juste une table de 0
un 1
où x = y
, cela peut être moins cher :
SELECT x, array_agg((x = y)::int) AS y_arr
FROM generate_series(1,10) x
, generate_series(1,10) y
GROUP BY 1
ORDER BY 1;
Violon SQL en s'appuyant sur celui que vous avez fourni dans les commentaires.
Notez que sqlfiddle.com a actuellement un bogue qui tue l'affichage des valeurs de tableau. J'ai donc converti en text
là pour contourner le problème.