在PostgreSQL中针对多种记录类型的generate_series


8

我有两个表,我想查询:pest_countspests看起来像:

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

Answers:


8

我通常通过为所有可能的数据点(此处为害虫和日期)建立表格来解决此类问题。这很容易通过来实现CROSS JOIN,请参见WITH下面的查询。

然后,作为结束步骤,我基于害虫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将与表date中的不匹配pest_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) ;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.