将生成的系列与选择中的每一行结合起来

Roc*_*nja 5 postgresql join

下面的选择返回我所有用户拥有的所有项目的数组。

WITH user_projects AS
(SELECT
  u.id as user_id
  ,COALESCE(array_agg(DISTINCT pa.project_id), '{0}'::integer[]) as project_ids
FROM users u
LEFT JOIN project_assignments pa on pa.user_creator_id = u.id
GROUP BY u.id
)

user: 1, project_ids: {1, 2}
user: 2, project_ids: {3, 4}
Run Code Online (Sandbox Code Playgroud)

使用该选择,我想进行报告,该报告将为每个用户显示 12 个月的摘要。

SELECT
  to_char(pk.created_at,'Mon') as mon
 ,extract(year from pk.created_at) as yyyy
 ,up.user_id
 ,COALESCE(count(distinct pk.keyword_id), 0) as total_keywords
FROM user_projects up
LEFT JOIN project_keywords pk on pk.project_id = ANY(up.project_ids::int[])
GROUP BY up.user_id, up.project_ids, 1, 2
Run Code Online (Sandbox Code Playgroud)

输出:

 mon: sept, yyyy: 2014, user_id: 1, total_keywords: 10,
 mon: , yyyy: , user_id: 2, total_keywords: 0,
Run Code Online (Sandbox Code Playgroud)

上面的选择正在做我需要的但没有显示所有 12 个月,只有它可以在其中找到数据的月份,对于project_keywords没有任何数据到 project_keywords 的用户,它将显示 0 并且月份和年份列将是空的。

那么如何为每个用户生成一份报告,显示自 以来的最后 12 个月now(),并且在没有可用数据的情况下填充月和年列?

我相信为了做到这一点,我需要一个 generate_series 它将返回过去 12 个月,所以我这样做了:

(select created_at from generate_series(now()::date - INTERVAL '1 year',now()::date,'1 month') as created_at)
Run Code Online (Sandbox Code Playgroud)

但是现在我被困住了,我不知道如何将所有这些结合起来。

预期的输出应该是

对于用户 1:

mon: sept, yyyy: 2013, user_id: 1, total_keywords: 10,
mon: oct, yyyy: 2013, user_id: 1, total_keywords: 11
...
mon: aug, yyyy: 2014, user_id: 1, total_keywords: 0
mon: sept, yyyy: 2014, user_id: 1, total_keywords: 2
Run Code Online (Sandbox Code Playgroud)

对于用户 2:

mon: sept, yyyy: 2013, user_id: 2, total_keywords: 10,
mon: oct, yyyy: 2013, user_id: 2, total_keywords: 0,
...
mon: aug, yyyy: 2014, user_id: 2, total_keywords: 0,
mon: sept, yyyy: 2014, user_id: 2, total_keywords: 0
Run Code Online (Sandbox Code Playgroud)

http://sqlfiddle.com/#!15/8c911/6

使用 PostgreSQL 9.3。

Cra*_*ger 7

你需要做left outer joingenerate_seriesON每个报告的日期相符的条款。

如果没有模式或示例数据,则对实际查询进行示例修改有点繁琐。

这是一个简化的示例,向您展示它是如何工作的:

CREATE TABLE sparse_dates(
    bakedgood text primary key,
    firstbaked date not null
);

INSERT INTO sparse_dates (bakedgood, firstbaked) VALUES
('tart', '2012-02-01'),
('baguette', '2012-02-01'),
('cookie', '2012-03-01'),
('macaron', '2012-08-01');
Run Code Online (Sandbox Code Playgroud)

要获取给定月份首次烘焙的烘焙食品数量:

SELECT monthtimestamp, count(bakedgood) 
FROM generate_series( 
    (SELECT min(firstbaked) FROM sparse_dates),
    (SELECT max(firstbaked) FROM sparse_dates),
    INTERVAL '1' MONTH) 
    AS monthtimestamp
LEFT OUTER JOIN sparse_dates ON (monthtimestamp = firstbaked)
GROUP BY monthtimestamp;
Run Code Online (Sandbox Code Playgroud)

输出如下:

     monthtimestamp     | count 
------------------------+-------
 2012-02-01 00:00:00+08 |     2
 2012-03-01 00:00:00+08 |     1
 2012-04-01 00:00:00+08 |     0
 2012-05-01 00:00:00+08 |     0
 2012-06-01 00:00:00+08 |     0
 2012-07-01 00:00:00+08 |     0
 2012-08-01 00:00:00+08 |     1
(7 rows)
Run Code Online (Sandbox Code Playgroud)