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.

War es hilfreich?

Lösung

Sie können die Liste aller ersten Tagen des vergangenen Jahres (sagen wir) mit

erstellen
select 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 Nutzung to_char() .

  • GROUP BY 1 ist Syntax Kurzschrift die erste Ausgabespalte zu referenzieren. Könnte auch GROUP BY day werden, aber das könnte mit einer vorhandenen Spalte mit dem gleichen Namen in Konflikt geraten. Oder GROUP 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 produziert NULL (0 für keine Zeilen), aber die LEFT JOIN tut.
    Um 0 statt NULL im äußeren SELECT zurückzukehren, verwenden Sie COALESCE(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.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top