【问题标题】:PostgreSQL query to count/group by day and display days with no dataPostgreSQL 查询按天计数/分组并显示没有数据的天数
【发布时间】:2013-03-19 10:32:36
【问题描述】:

我需要创建一个返回的 PostgreSQL 查询

  • 一天
  • 当天找到的对象数

重要的是每一天都出现在结果中,即使当天没有找到任何对象。 (这之前已经讨论过,但我无法在我的特定情况下让事情正常进行。)

首先,我找到了sql query to generate a range of days,我可以加入:

SELECT to_char(date_trunc('day', (current_date - offs)), 'YYYY-MM-DD')
AS date 
FROM generate_series(0, 365, 1) 
AS offs

结果:

    date    
------------
 2013-03-28
 2013-03-27
 2013-03-26
 2013-03-25
 ...
 2012-03-28
(366 rows)

现在我正在尝试将其加入一个名为“sharer_emailshare”的表中,该表有一个“已创建”列:

Table 'public.sharer_emailshare'
column    |   type  
-------------------
id        | integer
created   | timestamp with time zone
message   | text
to        | character varying(75)

这是我迄今为止最好的GROUP BY 查询:

SELECT d.date, count(se.id) FROM (
    select to_char(date_trunc('day', (current_date - offs)), 'YYYY-MM-DD')
    AS date 
    FROM generate_series(0, 365, 1) 
    AS offs
    ) d 
JOIN sharer_emailshare se 
ON (d.date=to_char(date_trunc('day', se.created), 'YYYY-MM-DD'))  
GROUP BY d.date;

结果:

    date    | count 
------------+-------
 2013-03-27 |    11
 2013-03-24 |     2
 2013-02-14 |     2
(3 rows)

期望的结果:

    date    | count 
------------+-------
 2013-03-28 |     0
 2013-03-27 |    11
 2013-03-26 |     0
 2013-03-25 |     0
 2013-03-24 |     2
 2013-03-23 |     0
 ...
 2012-03-28 |     0
(366 rows)

如果我理解正确,这是因为我使用的是普通的(隐含的 INNERJOIN,这是预期的行为,如 discussed in the postgres docs

我查看了几十个 StackOverflow 解决方案,所有具有工作查询的解决方案似乎都是特定于 MySQL/Oracle/MSSQL 的,我很难将它们转换为 PostgreSQL。

询问this question 的人使用 Postgres 找到了他的答案,但将其放在了一段时间前过期的 pastebin 链接上。

我尝试切换到LEFT OUTER JOINRIGHT JOINRIGHT OUTER JOINCROSS JOIN,如果为空,则使用CASE 语句来插入另一个值,COALESCE 以提供默认值等,但我无法以某种方式使用它们来满足我的需求。

感谢任何帮助!而且我保证我很快就会开始阅读那本 PostgreSQL 巨著;)

【问题讨论】:

    标签: sql postgresql join group-by


    【解决方案1】:

    您只需要 left outer join 而不是内部连接:

    SELECT d.date, count(se.id)
    FROM (SELECT to_char(date_trunc('day', (current_date - offs)), 'YYYY-MM-DD') AS date 
          FROM generate_series(0, 365, 1) AS offs
         ) d LEFT OUTER JOIN
         sharer_emailshare se 
         ON d.date = to_char(date_trunc('day', se.created), 'YYYY-MM-DD'))  
    GROUP BY d.date;
    

    【讨论】:

    • 哇,我是如此接近!当我在测试期间尝试 LEFT OUTER JOIN 时,我一定搞砸了。感谢您的快速回复!
    • 实际上,我认为问题在于我在其中添加了一个裸 WHERE 子句,我在这里没有提及。再次感谢!
    【解决方案2】:

    根据 Gordon Linoff 的回答,我意识到另一个问题是我有一个 WHERE 子句,我在原始问题中没有提到。

    我做了一个子查询,而不是赤裸裸的WHERE

    SELECT d.date, count(se.id) FROM (
        select to_char(date_trunc('day', (current_date - offs)), 'YYYY-MM-DD')
        AS date 
        FROM generate_series(0, 365, 1) 
        AS offs
        ) d 
    LEFT OUTER JOIN (
        SELECT * FROM sharer_emailshare 
        WHERE showroom_id=5
    ) se
    ON (d.date=to_char(date_trunc('day', se.created), 'YYYY-MM-DD')) 
    GROUP BY d.date;
    

    【讨论】:

      【解决方案3】:

      扩展 Gordon Linoff 的有用答案,我建议进行一些改进,例如:

      • 使用::date 而不是date_trunc('day', ...)
      • 加入日期类型而不是字符类型(更简洁)。
      • 使用特定的日期范围,以便日后更容易更改。在这种情况下,我选择了表中最近条目之前的一年 - 这是其他查询无法轻松完成的。
      • 计算任意子查询的总数(使用 CTE)。您只需要将感兴趣的列转换为日期类型并将其命名为 date_column。
      • 包含一个累计总计列。 (为什么不呢?)

      这是我的查询:

      WITH dates_table AS (
          SELECT created::date AS date_column FROM sharer_emailshare WHERE showroom_id=5
      )
      SELECT series_table.date, COUNT(dates_table.date_column), SUM(COUNT(dates_table.date_column)) OVER (ORDER BY series_table.date) FROM (
          SELECT (last_date - b.offs) AS date
              FROM (
                  SELECT GENERATE_SERIES(0, last_date - first_date, 1) AS offs, last_date from (
                       SELECT MAX(date_column) AS last_date, (MAX(date_column) - '1 year'::interval)::date AS first_date FROM dates_table
                  ) AS a
              ) AS b
      ) AS series_table
      LEFT OUTER JOIN dates_table
          ON (series_table.date = dates_table.date_column)
      GROUP BY series_table.date
      ORDER BY series_table.date
      

      我测试了查询,它产生了相同的结果,加上累积总计列。

      【讨论】:

      • 我喜欢你的解决方案 :) 我所要做的就是复制和粘贴,将 CTE 更改为我的表格,然后一切就绪。
      【解决方案4】:

      我将尝试提供包含一些解释的答案。我将从最小的构建块开始并逐步完成。

      如果您运行这样的查询:

      SELECT series.number FROM generate_series(0, 9) AS series(number)
      

      你会得到这样的输出:

       number 
      --------
            0
            1
            2
            3
            4
            5
            6
            7
            8
            9
      (10 rows)
      

      这可以变成这样的日期:

      SELECT CURRENT_DATE + sequential_dates.date AS date
        FROM generate_series(0, 9) AS sequential_dates(date)
      

      这将给出如下输出:

          date    
      ------------
       2019-09-29
       2019-09-30
       2019-10-01
       2019-10-02
       2019-10-03
       2019-10-04
       2019-10-05
       2019-10-06
       2019-10-07
       2019-10-08
      (10 rows)
      

      然后您可以执行这样的查询(例如),将原始查询作为子查询加入您最终感兴趣的任何表:

         SELECT sequential_dates.date,
                COUNT(calendar_items.*) AS calendar_item_count
           FROM (SELECT CURRENT_DATE + sequential_dates.date AS date
                   FROM generate_series(0, 9) AS sequential_dates(date)) sequential_dates
      LEFT JOIN calendar_items ON calendar_items.starts_at::date = sequential_dates.date
       GROUP BY sequential_dates.date
      

      这将给出如下输出:

          date    | calendar_item_count 
      ------------+---------------------
       2019-09-29 |                   1
       2019-09-30 |                   8
       2019-10-01 |                  15
       2019-10-02 |                  11
       2019-10-03 |                   1
       2019-10-04 |                  12
       2019-10-05 |                   0
       2019-10-06 |                   0
       2019-10-07 |                  27
       2019-10-08 |                  24
      

      【讨论】:

      • Generate_series 有重载版本generate_series(start, stop, step interval):dbfiddle.uk/…
      【解决方案5】:

      我喜欢 Jason Swett SQL,但遇到了一些日期的计数应该为零而不是一的问题。 从 public.post_call_info 运行语句 select count(*) where timestamp::date = '2020-11-23' count = 0,但下面等于 1。

      此外,+ 给我一个前进的时间表,所以改为减号提供当前日期之前 9 天的数据。

      SELECT sequential_dates.date,
      COUNT(*) AS call_count
      FROM (SELECT CURRENT_DATE - sequential_dates.date AS date
              FROM generate_series(0, 9) AS sequential_dates(date)) sequential_dates
      LEFT JOIN public.post_call_info ON public.post_call_info.timestamp::date = 
          sequential_dates.date
      GROUP BY sequential_dates.date
          order by date desc
      

      【讨论】:

      • 现在发现了这个问题 - 在计数下添加架构和表。*。这解决了计数问题。现在显示 0
      • 选择sequential_dates.date, COUNT(public.post_call_info.*) AS call_count FROM (SELECT CURRENT_DATE-sequential_dates.date AS date FROM generate_series(0, 9) 作为sequential_dates(date))sequential_dates LEFT JOIN public .post_call_info ON public.post_call_info.timestamp::date = sequence_dates.date GROUP BY sequence_dates.date 按日期顺序排列
      猜你喜欢
      • 2015-11-14
      • 2015-06-27
      • 2020-08-19
      • 2010-11-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多