2016-09-13 1 views
2

Je le tableau suivant appelé sample_events:Pourquoi cette requête crosstab() renvoie-t-elle des clés en double?

Column | Type 
--------+----- 
title | text 
date | date 

avec des valeurs:

title |  date 
-------+------------ 
ev1 | 2017-01-01 
ev2 | 2017-01-03 
ev3 | 2017-01-02 
ev4 | 2017-12-10 
ev5 | 2017-12-11 
ev6 | 2017-07-28 

Afin de créer un tableau croisé dynamique avec le nombre d'événements par mois, j'utilisé chaque année unique, la fonction crosstab dans la forme crosstab(text source_sql, text category_sql):

SELECT * FROM crosstab (
    'SELECT extract(year from date) AS year, 
     extract(month from date) AS month, count(*) 
    FROM sample_events 
    GROUP BY year, month' 
, 
    'SELECT * FROM generate_series(1, 12)' 
) AS (
    year int, jan int, feb int, mar int, 
    apr int, may int, jun int, jul int, 
    aug int, sep int, oct int, nov int, dec int 
) ORDER BY year; 

le résultat est comme suit et comme prévu:

year | jan | feb | mar | apr | may | jun | jul | aug | sep | oct | nov | dec 
------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+---- 
2017 | 3 |  |  |  |  |  | 1 |  |  |  |  | 2 

Maintenant, je voudrais créer un tableau croisé dynamique avec le nombre d'événements par jour de la semaine dans chaque semaine unique de l'année. J'ai essayé requête suivante:

SELECT * FROM crosstab (
    'SELECT extract(week from date) AS week, 
     extract(dow from date) AS day_of_week, count(*) 
    FROM sample_events 
    GROUP BY week, day_of_week' 
, 
    'SELECT * FROM generate_series(0, 6)' 
) AS (
    week int, sun int, mon int, tue int, 
    wed int, thu int, fri int, sat int 
) ORDER BY week; 

Le résultat est pas comme prévu:

week | sun | mon | tue | wed | thu | fri | sat 
------+-----+-----+-----+-----+-----+-----+----- 
    1 |  |  | 1 |  |  |  |  
    1 |  | 1 |  |  |  |  |  
    30 |  |  |  |  |  | 1 |  
    49 | 1 |  |  |  |  |  |  
    50 |  | 1 |  |  |  |  |  
    52 | 1 |  |  |  |  |  |  

Les six événements sont là, mais pour une raison quelconque, il est la valeur de la semaine en double. Je pensais que le résultat soit quelque chose comme:

week | sun | mon | tue | wed | thu | fri | sat 
------+-----+-----+-----+-----+-----+-----+----- 
    1 |  | 1 | 1 |  |  |  |  
    30 |  |  |  |  |  | 1 |  
    49 | 1 |  |  |  |  |  |  
    50 |  | 1 |  |  |  |  |  
    52 | 1 |  |  |  |  |  |  

Questions

1) Pourquoi les résultats de la dernière requête contiennent des valeurs de clé, mais l'ancien qui ne fonctionne pas?

2) Comment créer un tableau croisé dynamique avec unique valeurs hebdomadaires?

Répondre

1

crosstab() attend une entrée ordonnée. Vous devez ajouter ORDER BY dans l'entrée:

SELECT * FROM crosstab (
    'SELECT extract(week from date)::int AS week 
     , extract(dow from date)::int AS day_of_week 
     , count(*)::int 
    FROM sample_events 
    GROUP BY week, day_of_week 
    ORDER BY week, day_of_week' 
, 'SELECT generate_series(0, 6)' 
) AS (
    week int, sun int, mon int, tue int, 
    wed int, thu int, fri int, sat int 
); 

Ou tout simplement ORDER BY week.

Strictement parlant, les valeurs de la même clé (week dans l'exemple) doivent être groupés (entrer dans la séquence). Les clés ne doivent pas être commandées. Mais le moyen le plus simple et le moins cher d'y parvenir est ORDER BY (qui trie les clés en plus).

Ou bref:

SELECT * FROM crosstab (
    'SELECT extract(week from date)::int 
     , extract(dow from date)::int 
     , count(*)::int 
    FROM sample_events 
    GROUP BY 1, 2 
    ORDER BY 1, 2' -- or just ORDER BY 1 
, 'SELECT generate_series(0, 6)' 
) AS ... 

Votre premier exemple avec des mois arrive à travailler parce que les données d'entrée a mois en séquence. Mais cela peut se casser n'importe quand si l'ordre physique des lignes dans votre table change (VACUUM, UPDATE, ...). Vous ne pouvez jamais compter sur l'ordre physique des lignes dans une table relationnelle.

Plus explication: