postgresqlの複数のレコードタイプのgenerate_series


8

私は、クエリにしたい2つのテーブルがありますpest_countsし、pestsどのように見えます:

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);

postgresを使用generate_seriesして、日付シリーズで見つかった各タイプの害虫の数を表示します。

予期された結果

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
...

次のようなものが必要になることはわかっていますが、残りの方法が正確にわかりません。

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

回答:


8

私は通常、考えられるすべてのデータポイント(ここでは害虫と日付)のテーブルを設定することで、このような問題を解決します。これはで簡単に実現できます。以下CROSS JOINWITHクエリを参照してください。

次に、最後のステップとして、害虫IDと日付に基づいて既存の測定値に(外部で)結合します。オプションで、を介して欠落値のデフォルトを指定しますCOALESCE()

したがって、クエリ全体は次のとおりです。

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;

SQLFiddleで作業中に確認してください。

注:テーブルまたは生成されたシリーズのいずれかが大きい場合CROSS JOIN、CTE の内部を実行することはお勧めできません。(特定の日のデータがあるかどうかに関係なく、すべての行を具体化する必要があります)。この場合FROM、への現在の参照の代わりに括弧で囲まれたサブ結合として、句で同じことを行う必要がありdata_pointsます。このようにして、プランナは影響を受ける行とインデックスを使用する可能性についてよりよく理解します。この例ではCTEを使用していますが、見た目がすっきりしているためです。


0

次回は、fiddle.comを使用してオンラインスキーマを操作することをお勧めします。

generate_series関数はタイムスタンプのセットを返すため、関数の外で日付にキャストする必要があります。はテーブルのとtimestamp一致しないため、これは現在のクエリで必要です。datepest_counts

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)

私は次のようなものを提案します:

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) ;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.