左连接与嵌套选择和聚合函数

时间:2017-08-26 17:29:17

标签: sql postgresql join left-join aggregate-functions

问题

我有一个生成日期表,我想与另一个表(d)联系,这是一个特定事件发生的日期列表。

s

Wednesday 23rd August 2017
Thursday 24th August 2017
Friday 25th August 2017
Saturday 26th August 2017

d

day_created -------------------------------- count
Thursday 24th August 2017 ----------------    45 
Saturday 26th August 2017 ----------------    32

我想显示未发生事件的行,如果我只有表d,我就不能这样做。

我想要的东西看起来像是:

day_created -------------------------------- count
Wednesday 23rd August  ---------------------  0
Thursday 24th August 2017  ----------------  45 
Friday 25th August 2017  ------------------   0
Saturday 26th August 2017 ----------------   32

我尝试使用左连接加入,如下所示:

SELECT day_created, COUNT(d.day_created) as total_per_day
FROM 
(SELECT date_trunc('day', task_1.created_at) as day_created
FROM task_1
)
d
LEFT JOIN (
SELECT (generate_series('2017-05-01', current_date, '1 day'::INTERVAL)) as standard_date
)
s
ON d.day_created=s.standard_date
GROUP BY d.day_created
ORDER BY day_created DESC;

我没有收到错误但是连接不起作用(即它不返回count为null的日期)。它返回的是表d和计数的日期,但不包括出现0次的日期。

我已经绕圈子了解我需要在左表中创建表s(我认为!),但我会因为语法的新手而感到困惑。

这完全在PostgreSQL 9.5.8中。

2 个答案:

答案 0 :(得分:0)

基本上,你有LEFT JOIN倒退。这应该可行,还有其他一些简化和性能优化:

SELECT s.standard_date, COUNT(d.day_created) AS total_per_day
FROM   generate_series('2017-05-01', current_date, interval '1 day') s(standard_date)
LEFT   JOIN task_1 d ON d.day_created >= s.standard_date
                    AND d.day_created <  s.standard_date + interval '1 day'
GROUP  BY 1
ORDER  BY 1;

这个计算d中的行,就像您评论的那样。不值。

请注意generate_series()仍会返回timestamp with time zone,即使您将date值传递给它。您可能希望转换为date或使用to_char()进行格式化,以便在外部SELECT中显示。 (而不是原始时间戳的组和顺序,而不是格式化的字符串。)

根据实际未公开的表格定义,可能存在根据当前时区设置的极端情况。

相关:

答案 1 :(得分:0)

  

我有一个生成日期表

在真实数据库中,我们不存储生成的系列。我们只是在需要时生成它们。

  

我想与另一个表(d)联系,这是一个特定事件发生的日期列表。 [...] 我想显示未发生事件的行,如果我只有表d,我就无法做到。

不,你可以做到。

CREATE TABLE d(day_created, count) AS VALUES
  ('24 August 2017'::date, 45),
  ('26 August 2017'::date, 32);

SELECT day_created, coalesce(count,0)
FROM (
  SELECT d::date
  FROM generate_series(
    '2017-08-01'::timestamp without time zone,
    '2017-09-01'::timestamp without time zone,
    '1 day'
  ) AS gs(d)
) AS gs(day_created)
LEFT OUTER JOIN d USING(day_created)
ORDER BY day_created;

 day_created | coalesce 
-------------+----------
 2017-08-01  |        0
 2017-08-02  |        0
 2017-08-03  |        0
 2017-08-04  |        0
 2017-08-05  |        0
 2017-08-06  |        0
 2017-08-07  |        0
 2017-08-08  |        0
 2017-08-09  |        0
 2017-08-10  |        0
 2017-08-11  |        0
 2017-08-12  |        0
 2017-08-13  |        0
 2017-08-14  |        0
 2017-08-15  |        0
 2017-08-16  |        0
 2017-08-17  |        0
 2017-08-18  |        0
 2017-08-19  |        0
 2017-08-20  |        0
 2017-08-21  |        0
 2017-08-22  |        0
 2017-08-23  |        0
 2017-08-24  |       45
 2017-08-25  |        0
 2017-08-26  |       32
 2017-08-27  |        0
 2017-08-28  |        0
 2017-08-29  |        0
 2017-08-30  |        0
 2017-08-31  |        0
 2017-09-01  |        0
(32 rows)