Select 每个值每天一行
Select one row per day for each value
我在 PostgreSQL 9.4 中有一个 SQL 查询,虽然由于我从中提取数据的 tables 而变得更加复杂,但归结为以下几点:
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 时间段,此查询将是 运行,期望的结果是每个用户在一个月的每一天都有一行。
问题是可能不是每个用户每个月的每一天都有数据,并且此查询仅 returns 行有数据的日子。
是否有一些方法可以修改此查询,使其 returns 每个用户每天一行,即使某些行中没有数据(日期和用户除外)?
我尝试使用 generate_series()
进行连接,但这没有用 - 它可以使没有丢失天数,但不是每个用户。我真正需要的是 "for each user in list, generate series of (user,date)
records"
编辑:为了澄清,我正在寻找的最终结果是对于数据库中的每个用户 - 定义为用户 table 中的记录 - 我希望每个日期一行。因此,如果我在我的 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);
我选择了一个月的 运行 时间 window 结束“昨天”。您没有准确定义您的“月份”。
假设 entry_date
是数据类型 date
。
更新后的要求更简单
要获取 每个 用户在 users
table(而不是当前选择)和给定时间范围内的结果,它得到更简单。您不需要 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);
为什么要用这种特殊的方式调用 generate_series()
?
- Generating time series between two dates in PostgreSQL
并且最好使用 ISO 8601 date format (YYYY-MM-DD
),无论区域设置如何都可以。
我在 PostgreSQL 9.4 中有一个 SQL 查询,虽然由于我从中提取数据的 tables 而变得更加复杂,但归结为以下几点:
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 时间段,此查询将是 运行,期望的结果是每个用户在一个月的每一天都有一行。
问题是可能不是每个用户每个月的每一天都有数据,并且此查询仅 returns 行有数据的日子。
是否有一些方法可以修改此查询,使其 returns 每个用户每天一行,即使某些行中没有数据(日期和用户除外)?
我尝试使用 generate_series()
进行连接,但这没有用 - 它可以使没有丢失天数,但不是每个用户。我真正需要的是 "for each user in list, generate series of (user,date)
records"
编辑:为了澄清,我正在寻找的最终结果是对于数据库中的每个用户 - 定义为用户 table 中的记录 - 我希望每个日期一行。因此,如果我在我的 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);
我选择了一个月的 运行 时间 window 结束“昨天”。您没有准确定义您的“月份”。
假设 entry_date
是数据类型 date
。
更新后的要求更简单
要获取 每个 用户在 users
table(而不是当前选择)和给定时间范围内的结果,它得到更简单。您不需要 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);
为什么要用这种特殊的方式调用 generate_series()
?
- Generating time series between two dates in PostgreSQL
并且最好使用 ISO 8601 date format (YYYY-MM-DD
),无论区域设置如何都可以。