it-swarm.com.de

PostgreSQL-Abfrage zum Zählen / Gruppieren nach Tag und Anzeigen von Tagen ohne Daten

Ich muss eine PostgreSQL-Abfrage erstellen, die zurückgibt

  • ein Tag
  • die Anzahl der für diesen Tag gefundenen Objekte

Es ist wichtig, dass jeder einzelne Tag in den Ergebnissen erscheint , auch wenn an diesem Tag keine Objekte gefunden wurden. (Dies wurde bereits besprochen, aber in meinem speziellen Fall konnte ich die Dinge nicht zum Laufen bringen.)

Zuerst fand ich eine SQL-Abfrage, um einen Bereich von Tagen zu generieren , mit der ich beitreten kann:

SELECT to_char(date_trunc('day', (current_date - offs)), 'YYYY-MM-DD')
AS date 
FROM generate_series(0, 365, 1) 
AS offs

Ergebnisse in:

    date    
------------
 2013-03-28
 2013-03-27
 2013-03-26
 2013-03-25
 ...
 2012-03-28
(366 rows)

Jetzt versuche ich, das mit einer Tabelle namens 'sharer_emailshare' zu verbinden, die eine 'created'-Spalte hat:

Table 'public.sharer_emailshare'
column    |   type  
-------------------
id        | integer
created   | timestamp with time zone
message   | text
to        | character varying(75)

Hier ist die beste GROUP BY Abfrage, die ich bisher habe:

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;

Die Ergebnisse:

    date    | count 
------------+-------
 2013-03-27 |    11
 2013-03-24 |     2
 2013-02-14 |     2
(3 rows)

Gewünschten Erfolge:

    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)

Wenn ich das richtig verstehe, ist das so, weil ich ein einfaches (implizites INNER) JOIN verwende, und dies ist das erwartete Verhalten, wie in den Postgres-Dokumenten besprochen .

Ich habe Dutzende von StackOverflow-Lösungen durchgesehen, und alle mit funktionierenden Abfragen scheinen spezifisch für MySQL/Oracle/MSSQL zu sein, und es fällt mir schwer, sie in PostgreSQL zu übersetzen.

Der Typ, der diese Frage fragte, fand seine Antwort bei Postgres, stellte sie aber auf einen Pastebin-Link, der vor einiger Zeit abgelaufen war.

Ich habe versucht, zu LEFT OUTER JOIN, RIGHT JOIN, RIGHT OUTER JOIN, CROSS JOIN Zu wechseln. Verwenden Sie eine CASE -Anweisung, um einen anderen Wert einzugeben, wenn null, COALESCE, um einen Standardwert usw. bereitzustellen, aber ich konnte sie nicht so verwenden, dass ich das bekomme, was ich brauche.

Jede Hilfe wird geschätzt! Und ich verspreche, dass ich bald dieses riesige PostgreSQL-Buch lesen werde;)

53
Marcel Chastain

Du brauchst nur ein left outer join anstelle eines inneren Joins:

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;
47
Gordon Linoff

Als Erweiterung der hilfreichen Antwort von Gordon Linoff würde ich einige Verbesserungen vorschlagen, wie zum Beispiel:

  • Verwenden Sie ::date Anstelle von date_trunc('day', ...)
  • Schließen Sie sich einem Datumstyp und keinem Zeichentyp an (es ist sauberer).
  • Verwenden Sie bestimmte Datumsbereiche, damit sie später leichter geändert werden können. In diesem Fall wähle ich ein Jahr vor dem letzten Eintrag in der Tabelle aus - etwas, das mit der anderen Abfrage nicht einfach möglich gewesen wäre.
  • Berechnen Sie die Summen für eine beliebige Unterabfrage (mit einem CTE). Sie müssen nur die Spalte von Interesse auf den Datumstyp umwandeln und als date_column bezeichnen.
  • Fügen Sie eine Spalte für die kumulative Summe hinzu. (Warum nicht?)

Hier ist meine Frage:

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

Ich habe die Abfrage getestet und sie liefert dieselben Ergebnisse sowie die Spalte für die kumulierte Summe.

26
Travis

Aufgrund der Antwort von Gordon Linoff wurde mir klar, dass ein weiteres Problem darin bestand, dass ich eine WHERE -Klausel hatte, die ich in der ursprünglichen Frage nicht erwähnt hatte.

Anstelle eines nackten WHERE habe ich eine Unterabfrage gemacht:

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;
7
Marcel Chastain