Postgres - wie Zeilen mit 0 Zählung zurück für fehlende Daten?
-
19-08-2019 - |
Frage
Ich habe ungleichmäßig verteilten Daten (WRT Datum) für ein paar Jahre (2003-2008). Ich möchte Daten für einen bestimmten Satz von Start- und Enddatum abzufragen, durch eines der unterstützten Intervalle der Daten Gruppierung (Tag, Woche, Monat, Quartal, Jahr) in PostgreSQL 8.3 ( http://www.postgresql.org/docs/8.3/static/functions-datetime.html # FUNKTIONEN-DATETIME--TRUNC ).
Das Problem ist, dass einige der Abfragen Ergebnisse kontinuierlich über die erforderliche Zeit geben, wie diese:
select to_char(date_trunc('month',date), 'YYYY-MM-DD'),count(distinct post_id)
from some_table where category_id=1 and entity_id = 77 and entity2_id = 115
and date <= '2008-12-06' and date >= '2007-12-01' group by
date_trunc('month',date) order by date_trunc('month',date);
to_char | count
------------+-------
2007-12-01 | 64
2008-01-01 | 31
2008-02-01 | 14
2008-03-01 | 21
2008-04-01 | 28
2008-05-01 | 44
2008-06-01 | 100
2008-07-01 | 72
2008-08-01 | 91
2008-09-01 | 92
2008-10-01 | 79
2008-11-01 | 65
(12 rows)
aber einige von ihnen einige Intervalle verpassen, weil es keine Daten vorhanden sind, wie diese:
select to_char(date_trunc('month',date), 'YYYY-MM-DD'),count(distinct post_id)
from some_table where category_id=1 and entity_id = 75 and entity2_id = 115
and date <= '2008-12-06' and date >= '2007-12-01' group by
date_trunc('month',date) order by date_trunc('month',date);
to_char | count
------------+-------
2007-12-01 | 2
2008-01-01 | 2
2008-03-01 | 1
2008-04-01 | 2
2008-06-01 | 1
2008-08-01 | 3
2008-10-01 | 2
(7 rows)
Dabei gilt die erforderliche Suchresultates ist:
to_char | count
------------+-------
2007-12-01 | 2
2008-01-01 | 2
2008-02-01 | 0
2008-03-01 | 1
2008-04-01 | 2
2008-05-01 | 0
2008-06-01 | 1
2008-07-01 | 0
2008-08-01 | 3
2008-09-01 | 0
2008-10-01 | 2
2008-11-01 | 0
(12 rows)
Eine Zählung von 0 für Einträge fehlen.
Ich habe früher Diskussionen auf Stack-Überlauf gesehen, aber sie lösen nicht mein Problem scheint es, da meine Gruppierung Zeitraum eines ist (Tag, Woche, Monat, Quartal, Jahr) und entschied sich zur Laufzeit durch die Anwendung. So ein Ansatz wie kommen Sie links mit einem Kalender-Tabelle oder Sequenztabelle wird nicht helfen, denke ich.
Meine aktuelle Lösung ist in diesen Lücken in Python (in einer Turbogears App) mit dem Kalendermodul zu füllen.
Gibt es einen besseren Weg, dies zu tun.
Lösung
Sie können die Liste aller ersten Tagen des vergangenen Jahres (sagen wir) mit
erstellenselect distinct date_trunc('month', (current_date - offs)) as date
from generate_series(0,365,28) as offs;
date
------------------------
2007-12-01 00:00:00+01
2008-01-01 00:00:00+01
2008-02-01 00:00:00+01
2008-03-01 00:00:00+01
2008-04-01 00:00:00+02
2008-05-01 00:00:00+02
2008-06-01 00:00:00+02
2008-07-01 00:00:00+02
2008-08-01 00:00:00+02
2008-09-01 00:00:00+02
2008-10-01 00:00:00+02
2008-11-01 00:00:00+01
2008-12-01 00:00:00+01
Dann können Sie mit dieser Serie verbinden.
Andere Tipps
Diese Frage ist alt. Aber da mitbenutzen es als Master nahm für ein neues Duplikat Ich füge eine richtige Antwort.
Die richtige Lösung
SELECT *
FROM (
SELECT day::date
FROM generate_series(timestamp '2007-12-01'
, timestamp '2008-12-01'
, interval '1 month') day
) d
LEFT JOIN (
SELECT date_trunc('month', date_col)::date AS day
, count(*) AS some_count
FROM tbl
WHERE date_col >= date '2007-12-01'
AND date_col <= date '2008-12-06'
-- AND ... more conditions
GROUP BY 1
) t USING (day)
ORDER BY day;
-
Mit
LEFT JOIN
, natürlich. -
generate_series()
können eine Tabelle von Zeitstempel erzeugen on the fly, und sehr schnell. -
Es ist im Allgemeinen schneller zu aggregieren vor Sie beitreten. Ich habe vor kurzem einen Testfall auf sqlfiddle.com in dieser verwandten Antwort zu finden:
-
Guss die
timestamp
date
(::date
) für ein Basisformat. Für weitere Nutzungto_char()
. -
GROUP BY 1
ist Syntax Kurzschrift die erste Ausgabespalte zu referenzieren. Könnte auchGROUP BY day
werden, aber das könnte mit einer vorhandenen Spalte mit dem gleichen Namen in Konflikt geraten. OderGROUP BY date_trunc('month', date_col)::date
aber das ist zu lang für meinen Geschmack. -
Arbeiten mit den zur Verfügung stehenden Intervall Argumente für
count()
nie produziertNULL
(0
für keine Zeilen), aber dieLEFT JOIN
tut.
Um0
stattNULL
im äußerenSELECT
zurückzukehren, verwenden SieCOALESCE(some_count, 0) AS some_count
. das Handbuch. -
Für eine mehr generische Lösung oder frei wählbare Zeitintervalle betrachte diese eng verwandte Antwort:
Sie können eine temporäre Tabelle zur Laufzeit erstellen und links auf dem mitmachen. Das scheint am meisten Sinn zu machen.