【问题标题】:Join a count query on generate_series() and retrieve Null values as '0'在 generate_series() 上加入计数查询并将 Null 值检索为“0”
【发布时间】:2012-11-05 00:12:34
【问题描述】:

我想使用generate_series() 计算每个月的 ID。此查询适用于 PostgreSQL 9.1:

SELECT (to_char(serie,'yyyy-mm')) AS year, sum(amount)::int AS eintraege FROM (
    SELECT  
       COUNT(mytable.id) as amount,   
       generate_series::date as serie   
       FROM mytable  
    
    RIGHT JOIN generate_series(     
       (SELECT min(date_from) FROM mytable)::date,   
       (SELECT max(date_from) FROM mytable)::date,  
       interval '1 day') ON generate_series = date(date_from)  
       WHERE version = 1   
       GROUP BY generate_series       
       ) AS foo
GROUP BY Year   
ORDER BY Year ASC;  

这是我的输出:

"2006-12" | 4  
"2007-02" | 1  
"2007-03" | 1  

但我想得到的是这个输出(一月份的'0'值):

"2006-12" | 4  
"2007-01" | 0  
"2007-02" | 1  
"2007-03" | 1  

仍然应该列出没有id 的月份。
任何想法如何解决这个问题?

样本数据:

drop table if exists mytable;
create table mytable(id bigint, version smallint, date_from timestamp);
insert into mytable(id, version, date_from) values
(4084036, 1, '2006-12-22 22:46:35'),
(4084938, 1, '2006-12-23 16:19:13'),
(4084938, 2, '2006-12-23 16:20:23'),
(4084939, 1, '2006-12-23 16:29:14'),
(4084954, 1, '2006-12-23 16:28:28'),
(4250653, 1, '2007-02-12 21:58:53'),
(4250657, 1, '2007-03-12 21:58:53')
;

【问题讨论】:

    标签: sql postgresql join postgresql-9.1 generate-series


    【解决方案1】:

    解开、简化和固定,它可能看起来像这样:

    SELECT to_char(s.tag,'yyyy-mm') AS monat
         , count(t.id) AS eintraege
    FROM  (
       SELECT generate_series(min(date_from)::date
                            , max(date_from)::date
                            , interval '1 day'
              )::date AS tag
       FROM   mytable t
       ) s
    LEFT   JOIN mytable t ON t.date_from::date = s.tag AND t.version = 1   
    GROUP  BY 1
    ORDER  BY 1;
    

    db小提琴here

    在所有噪音、误导性标识符和非常规格式中,真正的问题隐藏在这里:

    WHERE version = 1
    

    您正确使用了RIGHT [OUTER] JOIN。但是添加一个WHERE 子句需要mytable 中的现有行会有效地将RIGHT [OUTER] JOIN 转换为[INNER] JOIN

    将该过滤器移到JOIN 条件中以使其工作。

    我在做的时候简化了一些其他的事情。

    更好,但

    SELECT to_char(mon, 'yyyy-mm') AS monat
         , COALESCE(t.ct, 0) AS eintraege
    FROM  (
       SELECT date_trunc('month', date_from)::date AS mon
            , count(*) AS ct
       FROM   mytable
       WHERE  version = 1     
       GROUP  BY 1
       ) t
    RIGHT JOIN (
       SELECT generate_series(date_trunc('month', min(date_from))
                            , max(date_from)
                            , interval '1 mon')::date
       FROM   mytable
       ) m(mon) USING (mon)
    ORDER  BY mon;
    

    db小提琴here

    先聚合后加入要便宜得多 - 每月加入一行而不是每天一行。

    GROUP BYORDER BY 基于date 值而不是渲染的text 更便宜。

    count(*)count(id) 快一点,而在 this 查询中是等价的。

    generate_series() 基于timestamp 而不是date 更快更安全。见:

    【讨论】:

    • 非常感谢您的回答,尤其是解释!你的回答解决了问题。
    • 谢谢:关于WHERE 子句的解释为我解决了一个稍微不同的问题!
    猜你喜欢
    • 2023-04-11
    • 1970-01-01
    • 1970-01-01
    • 2017-01-26
    • 1970-01-01
    • 1970-01-01
    • 2010-09-07
    • 2017-05-23
    • 2013-05-26
    相关资源
    最近更新 更多