ibr*_*ter 2 sql postgresql time-series cross-join generate-series
我在PostgreSQL 9.4中有一个SQL查询,虽然由于我从中提取数据的表格更复杂,但归结为以下内容:
SELECT entry_date, user_id, <other_stuff>
FROM <tables, joins, etc>
GROUP BY entry_date, user_id
WHERE <whatever limits I want, such as limiting the date range or users>
Run Code Online (Sandbox Code Playgroud)
结果是每个用户每天有一行,我有数据.通常,此查询将在一个月的entry_date期间运行,期望的结果是每个用户每月有一行.
问题是每个月的每个用户可能没有数据,并且此查询仅返回包含数据的天数的行.
是否有某种方法可以修改此查询,以便每个用户每天返回一行,即使某些行中没有数据(日期和用户除外)?
我尝试使用a进行连接generate_series(),但这不起作用 - 它可以使没有错过的日子,但不是每个用户.我真正需要的是"为列表中的每个用户生成一系列(user,date)记录"
编辑:澄清一下,我要找的最终结果是,对于数据库中的每个用户 - 定义为用户表中的记录 - 我希望每个日期有一行.因此,如果我在where子句中指定日期范围5/1/15-5/31/15,我希望每个用户有31行,即使该用户在该范围内没有数据,或者只有几个数据天.
generate_series()是对的想法.你可能没有得到正确的细节.可以像这样工作:
WITH cte AS (
SELECT entry_date, user_id, <other_stuff>
FROM <tables, joins, etc>
GROUP BY entry_date, user_id
WHERE <whatever limits I want>
)
SELECT *
FROM (SELECT DISTINCT user_id FROM cte) u
CROSS JOIN (
SELECT entry_date::date
FROM generate_series(current_date - interval '1 month'
, current_date - interval '1 day'
, interval '1 day') entry_date
) d
LEFT JOIN cte USING (user_id, entry_date);
Run Code Online (Sandbox Code Playgroud)
我选择了一个月结束"昨天"的运行时间窗口.您没有完全定义您的"月份".
假设entry_date是数据类型date.
要获得表中每个用户users(而不是当前选择)和给定时间范围的结果,它会变得更简单.您不需要CTE:
SELECT *
FROM (SELECT user_id FROM users) u
CROSS JOIN (
SELECT entry_date::date
FROM generate_series(timestamp '2015-05-01'
, timestamp '2015-05-31'
, interval '1 day') entry_date
) d
LEFT JOIN (
SELECT entry_date, user_id, <other_stuff>
FROM <tables, joins, etc>
GROUP BY entry_date, user_id
WHERE <whatever>
) t USING (user_id, entry_date);
Run Code Online (Sandbox Code Playgroud)
最好使用ISO 8601日期格式,无论您的语言环境设置如何,都可以使用.