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

PostgreSQL 9.3 :Tableau croisé dynamique dynamique

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 1x = 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.