65

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

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

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

首先,我找到了一个sql 查询来生成一系列 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”的表中,该表有一个“created”列:

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,这是预期的行为,如postgres docs 中所述

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

这个问题的人用 Postgres 找到了他的答案,但把它放在了一段时间前过期的 pastebin 链接上。

我试图切换到LEFT OUTER JOIN, RIGHT JOIN, RIGHT OUTER JOIN, CROSS JOIN, 如果为 null ,则使用CASE语句来插入另一个值,COALESCE以提供默认值等,但我无法以某种方式使用它们来获得我需要的东西。

任何帮助表示赞赏!我保证我很快就会开始阅读那本 PostgreSQL 巨著;)

4

5 回答 5

58

你只需要一个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;
于 2013-03-28T20:06:09.690 回答
38

扩展 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

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

于 2014-03-14T02:16:34.620 回答
7

根据 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;
于 2013-03-28T22:20:03.450 回答
7

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

如果您运行这样的查询:

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
于 2019-09-29T13:42:24.693 回答
0

我喜欢 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
于 2020-11-24T00:52:10.007 回答