generate_series für mehrere Datensatztypen in postgresql

8

Ich habe zwei Tabellen, die ich abfragen möchte: pest_countsund pestsdie so aussehen:

CREATE TABLE pests(id,name)
AS VALUES
  (1,'Thrip'),
  (2,'Fungus Gnosts');

CREATE TABLE pest_counts(id,pest_id,date,count)
AS VALUES
  (1,1,'2015-01-01'::date,14),
  (2,2,'2015-01-02'::date,5);

Ich möchte postgres 'verwenden generate_series, um die Nummer jedes Schädlingstyps anzuzeigen, der für die Datumsreihe gefunden wurde:

erwartete Ergebnisse

name         | date       | count
-------------+------------+-------
Thrip        | 2015-01-01 | 14
Thrip        | 2015-01-02 | 0
....
Fungus Gnats | 2015-01-01 | 0
Fungus Gnats | 2015-01-02 | 5
...

Ich weiß, dass ich so etwas wie Folgendes brauche, bin mir aber nicht ganz sicher, wie ich den Rest erledigen soll:

SELECT date FROM generate_series('2015-01-01'::date, '2015-12-31'::date, '1 day') date
Kyle Decot
quelle

Antworten:

8

Normalerweise löse ich solche Probleme, indem ich eine Tabelle für alle möglichen Datenpunkte (hier die Schädlinge und Daten) aufstelle. Dies wird leicht durch a erreicht CROSS JOIN, siehe die WITHAbfrage unten.

Als letzten Schritt füge ich dann einfach (äußerlich) die vorhandenen Messungen hinzu, basierend auf der Schädlings-ID und dem Datum - optional mit einem Standard für die fehlenden Werte über COALESCE().

Die ganze Abfrage lautet also:

WITH data_points AS (
    SELECT id, name, i::date
    FROM pests
    CROSS JOIN generate_series('2015-01-01'::date, '2015-01-05', '1 day') t(i)
) 
SELECT d.name, d.i, COALESCE(p.cnt, 0) 
FROM data_points AS d 
LEFT JOIN pest_counts AS p 
    ON d.id = p.pest_id 
    AND d.i = p.count_date;

Überprüfen Sie es bei der Arbeit an SQLFiddle .

Hinweis: Wenn entweder die Tabelle (n) oder die generierten Serien groß sind, ist es CROSS JOINmöglicherweise eine schlechte Idee , das Innere eines CTE durchzuführen. (Es müssen alle Zeilen materialisiert werden, unabhängig davon, ob Daten für einen bestimmten Tag vorliegen oder nicht). In diesem Fall sollte man dasselbe in der FROMKlausel tun , als Sub-Join in Klammern anstelle des aktuellen Verweises auf data_points. Auf diese Weise hat der Planer ein besseres Verständnis für die betroffenen Zeilen und die Möglichkeiten zur Verwendung von Indizes. Ich verwende den CTE im Beispiel, weil er für das Beispiel sauberer aussieht.

dezso
quelle
0

Ich werde vorschlagen, dass Sie das nächste Mal fiddle.com verwenden, um ein Online-Schema zum Spielen zu haben.

Die Funktion generate_series gibt eine Reihe von Zeitstempeln zurück, sodass Sie sie außerhalb der Funktion auf das Datum umwandeln müssen. Dies ist in der aktuellen Abfrage erforderlich, da die timestampnicht mit der datein der pest_countsTabelle übereinstimmt .

sandbox=# \df generate_series
   Schema   |      Name       |         Result data type          |                        Argument data types                         |  Type  
(...)
 pg_catalog | generate_series | SETOF timestamp without time zone | timestamp without time zone, timestamp without time zone, interval | normal
 pg_catalog | generate_series | SETOF timestamp with time zone    | timestamp with time zone, timestamp with time zone, interval       | normal
(6 rows)

Ich werde etwas vorschlagen wie:

SELECT p.name, pc.date, pc.count 
FROM generate_series('2015-01-01'::date, '2015-12-31'::date, '1 day') days 
join pest_counts pc ON (days::date = pc.date) 
join pests p ON (p.id = pc.pest_id) ;
3manuek
quelle