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

Requête d'analyse croisée PostgreSQL

Installez le module supplémentaire tablefunc une fois par base de données, qui fournit la fonction crosstab() . Depuis Postgres 9.1, vous pouvez utiliser CREATE EXTENSION pour cela :

CREATE EXTENSION IF NOT EXISTS tablefunc;

Cas de test amélioré

CREATE TABLE tbl (
   section   text
 , status    text
 , ct        integer  -- "count" is a reserved word in standard SQL
);

INSERT INTO tbl VALUES 
  ('A', 'Active', 1), ('A', 'Inactive', 2)
, ('B', 'Active', 4), ('B', 'Inactive', 5)
                    , ('C', 'Inactive', 7);  -- ('C', 'Active') is missing

Formulaire simple - ne convient pas aux attributs manquants

crosstab(text) avec 1 paramètre d'entrée :

SELECT *
FROM   crosstab(
   'SELECT section, status, ct
    FROM   tbl
    ORDER  BY 1,2'  -- needs to be "ORDER BY 1,2" here
   ) AS ct ("Section" text, "Active" int, "Inactive" int);

Renvoie :

Section
 Section | Active | Inactive
---------+--------+----------
 A       |      1 |        2
 B       |      4 |        5
 C       |      7 |           -- !!
  • Pas besoin de caster et de renommer.
  • Notez le incorrect résultat pour C :la valeur 7 est rempli pour la première colonne. Parfois, ce comportement est souhaitable, mais pas pour ce cas d'utilisation.
  • Le formulaire simple est également limité à exactement trois colonnes dans la requête d'entrée fournie :row_name , catégorie , valeur . Il n'y a pas de place pour les colonnes supplémentaires comme dans l'alternative à 2 paramètres ci-dessous.

Formulaire sécurisé

crosstab(text, text) avec 2 paramètres d'entrée :

SELECT *
FROM   crosstab(
   'SELECT section, status, ct
    FROM   tbl
    ORDER  BY 1,2'  -- could also just be "ORDER BY 1" here

  , $$VALUES ('Active'::text), ('Inactive')$$
   ) AS ct ("Section" text, "Active" int, "Inactive" int);

Renvoie :

Section
 Section | Active | Inactive
---------+--------+----------
 A       |      1 |        2
 B       |      4 |        5
 C       |        |        7  -- !!
  • Notez le résultat correct pour C .

  • Le deuxième paramètre peut être n'importe quelle requête renvoyant une ligne par attribut correspondant à l'ordre de la définition de colonne à la fin. Souvent, vous souhaiterez interroger des attributs distincts de la table sous-jacente comme ceci :

      'SELECT DISTINCT attribute FROM tbl ORDER BY 1'
    

C'est dans le manuel.

Étant donné que vous devez de toute façon épeler toutes les colonnes dans une liste de définition de colonne (sauf pour crosstabN() variantes), il est généralement plus efficace de fournir une courte liste dans un VALUES expression comme démontré :

    $$VALUES ('Active'::text), ('Inactive')$$)

Ou (pas dans le manuel) :

    $$SELECT unnest('{Active,Inactive}'::text[])$$  -- short syntax for long lists
  • J'ai utilisé des cotations en dollars pour faciliter la citation.

  • Vous pouvez même sortir des colonnes avec différentes types de données avec crosstab(text, text) - tant que la représentation textuelle de la colonne de valeur est une entrée valide pour le type cible. De cette façon, vous pourriez avoir des attributs de différents types et afficher text , date , numeric etc. pour les attributs respectifs. Il y a un exemple de code à la fin du chapitre crosstab(text, text) dans le manuel.

db<>jouez ici

Effet des lignes d'entrée excessives

Les lignes d'entrée excédentaires sont gérées différemment - lignes en double pour la même combinaison ("row_name", "category") - (section, status) dans l'exemple ci-dessus.

Le 1-paramètre Le formulaire remplit les colonnes de valeurs disponibles de gauche à droite. Les valeurs excédentaires sont ignorées.
Les premières lignes d'entrée prévalent.

Le 2 paramètres Le formulaire affecte chaque valeur d'entrée à sa colonne dédiée, écrasant toute affectation précédente.
Les lignes d'entrée ultérieures prévalent.

En règle générale, vous n'avez pas de doublons pour commencer. Mais si vous le faites, ajustez soigneusement l'ordre de tri à vos besoins - et documentez ce qui se passe.
Ou obtenez rapidement des résultats arbitraires si vous ne vous en souciez pas. Soyez juste conscient de l'effet.

Exemples avancés

  • Pivot sur plusieurs colonnes à l'aide de Tablefunc - démontrant également les "colonnes supplémentaires" mentionnées

  • Alternative dynamique au pivot avec CASE et GROUP BY


\crosstabview en psql

Postgres 9.6 a ajouté cette méta-commande à son terminal interactif par défaut psql. Vous pouvez exécuter la requête que vous utiliseriez comme premier crosstab() paramètre et alimentez-le dans \crosstabview (immédiatement ou à l'étape suivante). Comme :

db=> SELECT section, status, ct FROM tbl \crosstabview

Résultat similaire à celui ci-dessus, mais il s'agit d'une fonctionnalité de représentation côté client exclusivement. Les lignes d'entrée sont traitées légèrement différemment, d'où ORDER BY n'est pas requis. Détails pour \crosstabview dans le manuel. Il y a plus d'exemples de code au bas de cette page.

Réponse connexe sur dba.SE par Daniel Vérité (l'auteur de la fonctionnalité psql) :

  • Comment puis-je générer un CROSS JOIN pivoté où la définition de table résultante est inconnue ?