Postgres - 如何返回缺失数据计数为 0 的行?

Posted

技术标签:

【中文标题】Postgres - 如何返回缺失数据计数为 0 的行?【英文标题】:Postgres - how to return rows with 0 count for missing data? 【发布时间】:2010-09-25 15:48:01 【问题描述】:

我有几年(2003-2008)的数据分布不均(日期)。我想查询一组给定的开始和结束日期的数据,并按 PostgreSQL 8.3 (http://www.postgresql.org/docs/8.3/static/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC) 中支持的任何时间间隔(日、周、月、季度、年)对数据进行分组。

问题在于,某些查询会在要求的时间段内给出连续的结果, 作为这个:

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)

但他们中的一些人错过了一些间隔,因为没有数据存在,就像这个:

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)

所需的结果集在哪里:

  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)

缺失条目的计数为 0。

我已经看到早期关于 Stack Overflow 的讨论,但它们似乎并没有解决我的问题,因为我的分组期是(日、周、月、季度、年)之一,并由应用程序决定运行时。因此,我猜像左连接与日历表或序列表这样的方法无济于事。

我目前的解决方案是使用日历模块在 Python(在 Turbogears 应用中)填补这些空白。

有没有更好的方法来做到这一点。

【问题讨论】:

【参考方案1】:

这个问题很老了。但是由于其他用户选择它作为新副本的主人,我正在添加一个正确的答案。

适当的解决方案

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;

当然是LEFT JOIN

generate_series() 可以动态生成时间戳表,而且速度非常快。

在您加入之前聚合通常会更快。我最近在这个相关答案中在 sqlfiddle.com 上提供了一个测试用例:

PostgreSQL - order by an array

timestamp 转换为date (::date) 以获得基本格式。更多使用to_char()

GROUP BY 1 是引用第一个输出列的语法简写。也可以是GROUP BY day,但这可能与现有的同名列冲突。或者GROUP BY date_trunc('month', date_col)::date,但这对我来说太长了。

使用date_trunc() 的可用间隔参数。

count() never produces NULL0 表示没有行),但 LEFT JOIN 可以。 要在外部SELECT 中返回0 而不是NULL,请使用COALESCE(some_count, 0) AS some_count。 The manual.

对于更通用的解决方案或任意时间间隔,请考虑以下密切相关的答案:

Best way to count records by arbitrary time intervals in Rails+Postgres

【讨论】:

是否可以使用 CTE 来完成此任务? @zam6ak:当然,以各种方式。但你为什么想要? CTE 会更慢。只有在 Postgres 中需要它们时才使用 CTE。如果您有用例,我建议您提出一个新问题。您可以随时链接到此答案的上下文,并在此处发表评论,链接到相关问题并通知我。 谢谢,我发了一个新的question 这个查询正是我要找的,谢谢你的回答。有没有办法让 some_count 返回 0 而不是空数据? @Paul:是的,你错过了重要的部分。 COALESCE(count(*), 0) 从不 有意义。我写道:在外部SELECT 使用COALESCE(some_count, 0) AS some_countcount(*) 发生在 LEFT JOIN 之前的子查询中。【参考方案2】:

您可以使用

创建去年(比如说)所有第一天的列表
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

然后你就可以加入那个系列了。

【讨论】:

我试过这个:pastebin.com/f6f44e58b 仍然是 7 行。加入有什么问题吗 虽然我认为 (start, stop , step) 必须在进行查询之前仔细填写,并且我怀疑在足够长的时间内 28 的步骤可能会失败,你怎么看? 以28天为步长,每个月都会至少命中一次,因为最短的月份是28天;不过,请随意使用任何较小的步骤。它偶尔会在同一个月出现两次,因此使用了 DISTINCT 过滤器。 我希望你没有删除 pastebin 以获得正确答案:(【参考方案3】:

您可以在运行时创建一个临时表并在其上留下连接。这似乎是最有意义的。

【讨论】:

对不起,我没有时间研究实际可行性

以上是关于Postgres - 如何返回缺失数据计数为 0 的行?的主要内容,如果未能解决你的问题,请参考以下文章

如何按“熊猫”中的列获取缺失/NaN 数据的汇总计数?

如何按“熊猫”中的列获取缺失/NaN 数据的汇总计数?

如何从表中选择小时计数,包括缺失小时数?

如何使用NodeJ从Postgres返回INSERT命令中的所有数据

在 Postgres 中聚合多个字段时填写缺失的行

如何使此查询也返回计数值为 0 的行?