查找条件是否在一系列日期之间保持为真
Find if a condition stays true between a range of dates
我有一个 "action" table:: id, type, status, created, live_at, expired_at
+-----------+-----------+------------+-------------------------------+
| id | type | status | created | live_at | expired_at |
| (uuid) | (string) | (string) | (date) | (date) | (date) |
+-----------+-----------+------------+-------------------------------+
示例行是:
10f1dc79-61b7-46a4-ad66-55e2a68b7148 | FACEBOOK_SOCIAL_SHARE | EXPIRED | 2019-06-21 11:28:31 | 2019-07-21 11:28:36 | 2019-10-02 11:40:27
3e59ccd4-a795-4e74-b841-4da1e57fb51f | FACEBOOK_SOCIAL_SHARE | LIVE | 2019-10-04 18:25:57 | 2019-10-04 18:25:57 | NULL
我必须 运行 一个查询,我可以在其中获取按类型和月份分组的所有实时操作。
示例结果:
TYPE MONTH LIVE
FACEBOOK_SOCIAL_SHARE 7 1
FACEBOOK_SOCIAL_SHARE 8 1
FACEBOOK_SOCIAL_SHARE 9 5
FACEBOOK_SOCIAL_SHARE 10 9
问题是,如果某个操作在第 8 个月上线并在第 10 个月的某天过期,那么查询应该将该操作也计为在第 8、9 和 10 个月内上线。
我有一个问题,但它只会将此操作计为 8 月份的活动!
SELECT TYPE, EXTRACT(MONTH FROM action.live_at) AS month, count(distinct(action.id)) AS live
FROM "action" AS action
WHERE action.live_at IS NOT NULL
GROUP BY TYPE, EXTRACT(MONTH FROM action.live_at)
任何帮助将不胜感激。
据我了解你的问题,我认为以下内容可以满足你的要求:
with actions as (
select id, type,
array(select extract(month from x.dt)::int
from generate_series(date_trunc('month', live_at),
date_trunc('month', coalesce(expired_at, current_timestamp)) + interval '1 month' - interval '1 day',
interval '1 month') as x(dt)) as months_live
from action
)
select m.month, type, count(distinct a.id)
from generate_series(1,12) as m(month)
left join actions a on m.month = any(a.months_live)
group by m.month, type;
CTE 为 action
table 中的每一行生成所有月份的数组。因此,对于您的两个示例行,这将 return
id | months_live
-------------------------------------+------------
10f1dc79-61b7-46a4-ad66-55e2a68b7148 | {7,8,9,10}
3e59ccd4-a795-4e74-b841-4da1e57fb51f | {10,11}
表达式 date_trunc('month', coalesce(expired_at, current_timestamp)) + interval '1 month' - interval '1 day'
生成 expired_at
包含的月份的最后一天。这是必要的,以便 generate_series()
也包括那个月。
我现在不知道应该如何处理 expired_at
列中的 null
值 - 上面的表达式只是使用 "today" 然后。
外部查询然后在 12 个月的列表和操作之间进行外部连接 - 由于连接条件基于数组,操作 table 中的一行重复多次,因为连接条件匹配多次。
外部联接(不分组)return以下行(基于您的两个样本行,今天是 11 月的一天):
month | type | id
------+-----------------------+-------------------------------------
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | FACEBOOK_SOCIAL_SHARE | 10f1dc79-61b7-46a4-ad66-55e2a68b7148
8 | FACEBOOK_SOCIAL_SHARE | 10f1dc79-61b7-46a4-ad66-55e2a68b7148
9 | FACEBOOK_SOCIAL_SHARE | 10f1dc79-61b7-46a4-ad66-55e2a68b7148
10 | FACEBOOK_SOCIAL_SHARE | 10f1dc79-61b7-46a4-ad66-55e2a68b7148
10 | FACEBOOK_SOCIAL_SHARE | 3e59ccd4-a795-4e74-b841-4da1e57fb51f
11 | FACEBOOK_SOCIAL_SHARE | 3e59ccd4-a795-4e74-b841-4da1e57fb51f
12 | |
此结果然后按月份和类型分组,以便能够计算 ID。
因此您的两个示例行将 return:
month | type | count
------+-----------------------+------
1 | | 0
2 | | 0
3 | | 0
4 | | 0
5 | | 0
6 | | 0
7 | FACEBOOK_SOCIAL_SHARE | 1
8 | FACEBOOK_SOCIAL_SHARE | 1
9 | FACEBOOK_SOCIAL_SHARE | 1
10 | FACEBOOK_SOCIAL_SHARE | 2
11 | FACEBOOK_SOCIAL_SHARE | 1
12 | | 0
在线示例:https://rextester.com/NYUV51842
如果您经常需要它,请考虑编写一个函数:
create or replace function get_month_list(p_start timestamp, p_end timestamp)
returns int[]
as
$$
select array(select extract(month from x.dt)::int
from generate_series(date_trunc('month', p_start),
date_trunc('month', coalesce(p_end, current_timestamp)) + interval '1 month' - interval '1 day',
interval '1 month') as x(dt));
$$
language sql
immutable;
那么查询更容易阅读:
select m.month, type, count(distinct a.id)
from generate_series(1,12) as m(month)
left join action a on m.month = any(get_month_list(a.live_at, a.expired_at))
group by m.month, type;
我有一个 "action" table:: id, type, status, created, live_at, expired_at
+-----------+-----------+------------+-------------------------------+
| id | type | status | created | live_at | expired_at |
| (uuid) | (string) | (string) | (date) | (date) | (date) |
+-----------+-----------+------------+-------------------------------+
示例行是:
10f1dc79-61b7-46a4-ad66-55e2a68b7148 | FACEBOOK_SOCIAL_SHARE | EXPIRED | 2019-06-21 11:28:31 | 2019-07-21 11:28:36 | 2019-10-02 11:40:27
3e59ccd4-a795-4e74-b841-4da1e57fb51f | FACEBOOK_SOCIAL_SHARE | LIVE | 2019-10-04 18:25:57 | 2019-10-04 18:25:57 | NULL
我必须 运行 一个查询,我可以在其中获取按类型和月份分组的所有实时操作。
示例结果:
TYPE MONTH LIVE
FACEBOOK_SOCIAL_SHARE 7 1
FACEBOOK_SOCIAL_SHARE 8 1
FACEBOOK_SOCIAL_SHARE 9 5
FACEBOOK_SOCIAL_SHARE 10 9
问题是,如果某个操作在第 8 个月上线并在第 10 个月的某天过期,那么查询应该将该操作也计为在第 8、9 和 10 个月内上线。
我有一个问题,但它只会将此操作计为 8 月份的活动!
SELECT TYPE, EXTRACT(MONTH FROM action.live_at) AS month, count(distinct(action.id)) AS live
FROM "action" AS action
WHERE action.live_at IS NOT NULL
GROUP BY TYPE, EXTRACT(MONTH FROM action.live_at)
任何帮助将不胜感激。
据我了解你的问题,我认为以下内容可以满足你的要求:
with actions as (
select id, type,
array(select extract(month from x.dt)::int
from generate_series(date_trunc('month', live_at),
date_trunc('month', coalesce(expired_at, current_timestamp)) + interval '1 month' - interval '1 day',
interval '1 month') as x(dt)) as months_live
from action
)
select m.month, type, count(distinct a.id)
from generate_series(1,12) as m(month)
left join actions a on m.month = any(a.months_live)
group by m.month, type;
CTE 为 action
table 中的每一行生成所有月份的数组。因此,对于您的两个示例行,这将 return
id | months_live
-------------------------------------+------------
10f1dc79-61b7-46a4-ad66-55e2a68b7148 | {7,8,9,10}
3e59ccd4-a795-4e74-b841-4da1e57fb51f | {10,11}
表达式 date_trunc('month', coalesce(expired_at, current_timestamp)) + interval '1 month' - interval '1 day'
生成 expired_at
包含的月份的最后一天。这是必要的,以便 generate_series()
也包括那个月。
我现在不知道应该如何处理 expired_at
列中的 null
值 - 上面的表达式只是使用 "today" 然后。
外部查询然后在 12 个月的列表和操作之间进行外部连接 - 由于连接条件基于数组,操作 table 中的一行重复多次,因为连接条件匹配多次。
外部联接(不分组)return以下行(基于您的两个样本行,今天是 11 月的一天):
month | type | id
------+-----------------------+-------------------------------------
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | FACEBOOK_SOCIAL_SHARE | 10f1dc79-61b7-46a4-ad66-55e2a68b7148
8 | FACEBOOK_SOCIAL_SHARE | 10f1dc79-61b7-46a4-ad66-55e2a68b7148
9 | FACEBOOK_SOCIAL_SHARE | 10f1dc79-61b7-46a4-ad66-55e2a68b7148
10 | FACEBOOK_SOCIAL_SHARE | 10f1dc79-61b7-46a4-ad66-55e2a68b7148
10 | FACEBOOK_SOCIAL_SHARE | 3e59ccd4-a795-4e74-b841-4da1e57fb51f
11 | FACEBOOK_SOCIAL_SHARE | 3e59ccd4-a795-4e74-b841-4da1e57fb51f
12 | |
此结果然后按月份和类型分组,以便能够计算 ID。
因此您的两个示例行将 return:
month | type | count
------+-----------------------+------
1 | | 0
2 | | 0
3 | | 0
4 | | 0
5 | | 0
6 | | 0
7 | FACEBOOK_SOCIAL_SHARE | 1
8 | FACEBOOK_SOCIAL_SHARE | 1
9 | FACEBOOK_SOCIAL_SHARE | 1
10 | FACEBOOK_SOCIAL_SHARE | 2
11 | FACEBOOK_SOCIAL_SHARE | 1
12 | | 0
在线示例:https://rextester.com/NYUV51842
如果您经常需要它,请考虑编写一个函数:
create or replace function get_month_list(p_start timestamp, p_end timestamp)
returns int[]
as
$$
select array(select extract(month from x.dt)::int
from generate_series(date_trunc('month', p_start),
date_trunc('month', coalesce(p_end, current_timestamp)) + interval '1 month' - interval '1 day',
interval '1 month') as x(dt));
$$
language sql
immutable;
那么查询更容易阅读:
select m.month, type, count(distinct a.id)
from generate_series(1,12) as m(month)
left join action a on m.month = any(get_month_list(a.live_at, a.expired_at))
group by m.month, type;