J'ai besoin de créer une requête PostgreSQL qui renvoie
Il est important que chaque jour apparaisse dans les résultats , même si aucun objet n'a été trouvé ce jour-là. (Cela a été discuté auparavant, mais je n'ai pas pu faire fonctionner les choses dans mon cas spécifique.)
Tout d'abord, j'ai trouvé une requête SQL pour générer une plage de jours , avec laquelle je peux rejoindre:
SELECT to_char(date_trunc('day', (current_date - offs)), 'YYYY-MM-DD')
AS date
FROM generate_series(0, 365, 1)
AS offs
Résulte en:
date
------------
2013-03-28
2013-03-27
2013-03-26
2013-03-25
...
2012-03-28
(366 rows)
Maintenant, j'essaye de joindre cela à une table nommée 'sharer_emailshare' qui a une colonne 'créée':
Table 'public.sharer_emailshare'
column | type
-------------------
id | integer
created | timestamp with time zone
message | text
to | character varying(75)
Voici le meilleur GROUP BY
requête que j'ai jusqu'à présent:
SELECT d.date, count(se.id) FROM (
select to_char(date_trunc('day', (current_date - offs)), 'YYYY-MM-DD')
AS date
FROM generate_series(0, 365, 1)
AS offs
) d
JOIN sharer_emailshare se
ON (d.date=to_char(date_trunc('day', se.created), 'YYYY-MM-DD'))
GROUP BY d.date;
Les resultats:
date | count
------------+-------
2013-03-27 | 11
2013-03-24 | 2
2013-02-14 | 2
(3 rows)
Résultats désirés:
date | count
------------+-------
2013-03-28 | 0
2013-03-27 | 11
2013-03-26 | 0
2013-03-25 | 0
2013-03-24 | 2
2013-03-23 | 0
...
2012-03-28 | 0
(366 rows)
Si je comprends bien, c'est parce que j'utilise un simple (implicite INNER
) JOIN
, et c'est le comportement attendu, comme discuté dans les documents postgres .
J'ai parcouru des dizaines de solutions StackOverflow, et toutes celles avec des requêtes de travail semblent spécifiques à MySQL/Oracle/MSSQL et j'ai du mal à les traduire en PostgreSQL.
Le gars qui a demandé cette question a trouvé sa réponse, avec Postgres, mais l'a mise sur un lien Pastebin qui a expiré il y a quelque temps.
J'ai essayé de passer à LEFT OUTER JOIN
, RIGHT JOIN
, RIGHT OUTER JOIN
, CROSS JOIN
, utilisez une instruction CASE
pour saisir une autre valeur si null, COALESCE
pour fournir une valeur par défaut, etc., mais je n'ai pas pu les utiliser d'une manière qui me permette Ce dont j'ai besoin.
Toute aide est appréciée! Et je promets que je vais bientôt lire ce livre géant de PostgreSQL;)
Vous avez juste besoin d'un left outer join
au lieu d'une jointure interne:
SELECT d.date, count(se.id)
FROM (SELECT to_char(date_trunc('day', (current_date - offs)), 'YYYY-MM-DD') AS date
FROM generate_series(0, 365, 1) AS offs
) d LEFT OUTER JOIN
sharer_emailshare se
ON d.date = to_char(date_trunc('day', se.created), 'YYYY-MM-DD'))
GROUP BY d.date;
Prolongeant la réponse utile de Gordon Linoff, je suggérerais quelques améliorations telles que:
::date
Au lieu de date_trunc('day', ...)
Voici ma requête:
WITH dates_table AS (
SELECT created::date AS date_column FROM sharer_emailshare WHERE showroom_id=5
)
SELECT series_table.date, COUNT(dates_table.date_column), SUM(COUNT(dates_table.date_column)) OVER (ORDER BY series_table.date) FROM (
SELECT (last_date - b.offs) AS date
FROM (
SELECT GENERATE_SERIES(0, last_date - first_date, 1) AS offs, last_date from (
SELECT MAX(date_column) AS last_date, (MAX(date_column) - '1 year'::interval)::date AS first_date FROM dates_table
) AS a
) AS b
) AS series_table
LEFT OUTER JOIN dates_table
ON (series_table.date = dates_table.date_column)
GROUP BY series_table.date
ORDER BY series_table.date
J'ai testé la requête, et elle produit les mêmes résultats, plus la colonne pour le total cumulé.
Sur la base de la réponse de Gordon Linoff, j'ai réalisé qu'un autre problème était que j'avais une clause WHERE
que je n'avais pas mentionnée dans la question d'origine.
Au lieu d'un WHERE
nu, j'ai fait une sous-requête:
SELECT d.date, count(se.id) FROM (
select to_char(date_trunc('day', (current_date - offs)), 'YYYY-MM-DD')
AS date
FROM generate_series(0, 365, 1)
AS offs
) d
LEFT OUTER JOIN (
SELECT * FROM sharer_emailshare
WHERE showroom_id=5
) se
ON (d.date=to_char(date_trunc('day', se.created), 'YYYY-MM-DD'))
GROUP BY d.date;