每个值每天选择一行

时间:2015-06-23 21:17:32

标签: 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>

结果是每个用户每天有一行,我有数据。通常,此查询将在一个月的entry_date期间运行,期望的结果是每个用户每月有一行。

问题是每月每个用户可能没有数据,而且此查询仅返回包含数据的天数。

是否有某种方法可以修改此查询,以便每个用户每天返回一行,即使某些行中没有数据(日期和用户除外)?

我尝试使用generate_series()进行联接,但这不起作用 - 它可以使没有错过的日子,但不是每个用户。我真正需要的是“对于列表中的每个用户,生成一系列(user,date)记录”

编辑:澄清一下,我要找的最终结果是,对于数据库中的每个用户 - 定义为用户表中的记录 - 我希望每个日期有一行。因此,如果我在where子句中指定日期范围5/1 / 15-5 / 31/15,我希望每个用户有31行,即使该用户在该范围内没有数据,或者只有几个数据天。

1 个答案:

答案 0 :(得分:6)

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);

我选择了一个月结束的运行时间窗口&#34;昨天&#34;。你没有定义你的&#34;月&#34;准确。

假设entry_date为数据类型date

更新您的更新要求

要获得每个用户在users表中的结果(而不是当前选择)以及给定时间范围内的结果,它会变得更简单。你不需要CTE:

SELECT *
FROM   (SELECT user_id FROM users) u
CROSS  JOIN (
   SELECT entry_date::date 
   FROM   generate_series('2015-05-01'::date
                        , '2015-05-31'::date
                        , 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 limits I want>
   ) t USING (user_id, entry_date);

最好使用无论您的语言环境设置如何工作的ISO 8601 date format