Her*_*sas 5 sql postgresql window-functions gaps-and-islands
给定一个像这样的表:
| 人物ID | 联系日 | 最后联系天数 | 破折号组 |
|---|---|---|---|
| 1 | 2015-02-09 | 1 | |
| 1 | 2015-05-01 | 81 | 2 |
| 1 | 2015-05-02 | 1 | 2 |
| 1 | 2015-05-03 | 1 | 2 |
| 1 | 2015-06-01 | 29 | 3 |
| 1 | 2015-08-01 | 61 | 4 |
| 1 | 2015-08-04 | 3 | 4 |
| 1 | 2015-09-01 | 28 | 5 |
| 2 | 2015-05-01 | 1 | |
| 2 | 2015-06-01 | 31 | 2 |
| 2 | 2015-07-01 | 30 | 3 |
| 3 | 2015-05-01 | 1 | |
| 3 | 2015-05-02 | 1 | 1 |
| 3 | 2015-05-04 | 2 | 1 |
| 3 | 2015-06-01 | 28 | 2 |
| 3 | 2015-06-02 | 1 | 2 |
| 3 | 2015-06-06 | 4 | 3 |
另请参阅DB Fiddle 示例。
如何识别连续几天的连续但允许最大差距?
数据中的原始列是person_id和contact_day。我想按person_id“streak”(附近的几天)进行分区。到目前为止,我的方法是首先计算自上次联系以来的天数 ( days_last_contact),然后尝试使用它来计算列dash_group,该列标记最大阈值内的行 - 在示例中为 3 天。
我该如何计算dash_group?我days_last_contact通过减法计算contact_day,它是 1-lag,按 person_id 分区并按日期排序)。
SELECT
contact_day - lag(contact_day, 1, NULL)
OVER (PARTITION BY person_id ORDER BY contact_day ASC)
AS days_last_contact
FROM mydata
;
Run Code Online (Sandbox Code Playgroud)
但是我怎样才能使用它来将低于某个阈值的行分组在一起呢days_last_contact?(本例中为 3 天)。因此,在此示例中,dash_group2 for person_id1 标识了临近的 5 月 1 日、2 日和 3 日,但该人的下一个日期是 6 月 1 日,这太远了(距离上次联系已过去 29 天,比阈值为 3),因此它得到一个新的dash_group. 相似地,dash_group 4 将 8 月 1 日和 8 月 4 日分组在一起,因为差异为 3,但在 6 月 2 日和 6 月 6 日(人 3)的情况下,差异为 4,然后将它们分为不同的组。
环顾四周后,我发现了例如这个 SO 问题,他们指向这里的“技巧”#4,这非常hacky,但仅适用于连续日期/无间隙系列,并且我需要允许任意间隙。
计算第二个窗口函数中的间隙(大于给定的容差)即可形成您所追求的组数:
SELECT person_id, contact_day
, count(*) FILTER (WHERE gap > 3) OVER (PARTITION BY person_id ORDER BY contact_day) AS dash_group
FROM (
SELECT person_id, contact_day
, contact_day - lag(contact_day) OVER (PARTITION BY person_id ORDER BY contact_day) AS gap
FROM mydata
) sub
ORDER BY person_id, contact_day; -- optional
Run Code Online (Sandbox Code Playgroud)
db<>在这里摆弄
关于聚合FILTER子句:
它简短而直观,而且通常速度最快。看:
这就是“间隙与岛屿”的经典话题。一旦您知道要查找标签“ gaps-and-islands”,您就会发现大量相关或几乎相同的问题和答案,例如:
ETC。
我现在做了相应的标记。
| 归档时间: |
|
| 查看次数: |
713 次 |
| 最近记录: |