2017-06-21 3 views
1

Je veux créer un tableau croisé dynamique montrant le mois sur le mois la somme des réservations pour chaque travel_mode.PostgreSQL crosstab() alternative avec CASE et les agrégats

Tableau bookings:

timestamp 
, bookings 
, provider_id 

Tableau providers:

provider_id 
, travel_mode 

fonction Tableau croisé dynamique et les fonctions de tableau croisé ne doivent pas être utilisés pour le faire. Donc j'essaie d'utiliser JOIN et CASE. Voici la requête:

SELECT b.month, 
    (CASE WHEN p.travel_mode=train then b.amount end)train, 
    (CASE WHEN p.travel_mode=bus then b.amount end)bus, 
    (CASE WHEN p.travel_mode=air then b.amount end)air 
    FROM 
    (SELECT to_char(date_,month) as month, travel_mode, sum(bookings) as amount 
    from bookings as b 
    join providers as p 
    on b.provider_id=p.provider_id 
    group by b.month, p.travel_mode) 
    group by b.month; 

Cependant, je reçois une erreur qui dit:

subquery in FROM must have an alias LINE 6: 

Et quand j'ajoute un alias, il renvoie une erreur en disant:

column p.travel_mode must appear in the GROUP BY clause or be used in an aggregate function 
LINE 2: 

Le résultat final devrait être quelque chose comme ça

Month  Air    Bus   Train 
01   Amount(air)  Amount(Bus) Amount(train) 

J'ai l'impression que c'est une erreur mineure quelque part, mais je suis incapable de comprendre du tout.

P.S. J'ai dû supprimer toutes les citations dans la question car cela ne me permettait pas de publier ceci. Mais ceux-ci sont pris en charge dans la requête réelle.

+0

Vous pouvez (et devriez) publier tous vos devis. Cela le permet certainement. Vous devez toujours ajouter la définition de table réelle (instruction CREATE TABLE) montrant les types de données et les contraintes ainsi que votre version de Postgres. –

Répondre

0

Problèmes multiples. L'alias de table manquante est juste l'un d'entre eux. Cette requête devrait fonctionner:

SELECT month 
    , sum(CASE WHEN travel_mode = 'train' THEN amount END) AS train 
    , sum(CASE WHEN travel_mode = 'bus' THEN amount END) AS bus 
    , sum(CASE WHEN travel_mode = 'air' THEN amount END) AS air 
FROM (
    SELECT to_char(timestamp, 'MM') AS month, travel_mode, sum(bookings) AS amount 
    FROM bookings b 
    JOIN providers p USING (provider_id) 
    GROUP BY month, p.travel_mode 
    ) sub 
GROUP BY month; 
  • manquantes guillemets simples pour les chaînes littérales. (Vous semblez avoir supprimé ceux qui avaient la mauvaise impression que vous ne pouviez pas publier de citations.)

  • Alias ​​de table manquant pour la sous-requête - tout comme le dit le message d'erreur.

  • Dans la requête externe, les noms de table (ou alias) des tables sous-jacentes de la sous-requête ne sont pas visibles. Seul l'alias de la sous-requête est. Comme il n'y a que une sous-requête, vous n'avez absolument pas besoin de qualification de table. Est un nom de colonne de sortie (pas dans la table sous-jacente), donc la qualification de table b.month était également erronée.

  • Vous semblez vouloir des nombres à deux chiffres pendant des mois. Utilisez le template pattern 'MM' au lieu de 'mois' avec to_char().

  • L'agrégation dans la requête externe ne fonctionne pas comme vous l'aviez, comme le dit votre deuxième message d'erreur.Vous devez envelopper l'expression externe CASE dans une fonction d'agrégation. Vous pouvez également utiliser min() ou max() dans ce cas, car il n'y a jamais plus d'une ligne après la sous-requête.

  • Vous ne savez toujours pas d'où provient date_? Vous voulez dire timestamp? (ce qui n'est pas un bon identifiant)

Mais vous n'avez pas besoin du sous-requête pour commencer et peut simplifier à:

SELECT to_char(timestamp, 'MM') AS month 
    , sum(CASE WHEN p.travel_mode = 'train' THEN b.bookings END) AS train 
    , sum(CASE WHEN p.travel_mode = 'bus' THEN b.bookings END) AS bus 
    , sum(CASE WHEN p.travel_mode = 'air' THEN b.bookings END) AS air 
FROM bookings b 
JOIN providers p USING (provider_id) 
GROUP BY 1; 

Pour des performances optimales, vous devriez toujours utiliser crosstab(), cependant:

+0

La première requête n'a pas fonctionné. C'est l'erreur qu'il lance ERREUR: la colonne b.month n'existe pas LIGNE 9: GROUP BY b.month, p.travel_mode^ – justanothernewbie

+0

@justanothernewbie: C'est corrigé maintenant. Essayez la dernière version. ('mois' est un nom de colonne de sortie, donc la qualification de table était incorrecte.) –

+0

Cela a fonctionné parfaitement. Merci beaucoup :) – justanothernewbie

0

Vous avez de nommer le sous-requête que le message d'erreur indique:

SELECT b.month, 
(CASE WHEN p.travel_mode=train then b.amount end)train, 
(CASE WHEN p.travel_mode=bus then b.amount end)bus, 
(CASE WHEN p.travel_mode=air then b.amount end)air 
FROM 
(SELECT to_char(date_,month) as month, travel_mode, sum(bookings) as amount 
from bookings as b 
join providers as p 
on b.provider_id=p.provider_id 
group by b.month, p.travel_mode) 
**as foo** group by b.month; 

Retirez les étoiles pour le faire fonctionner.

+0

Ne fonctionne toujours pas, cependant. –