Postgres获取具有不同值的下一行

时间:2019-08-06 08:57:09

标签: sql postgresql date window-functions

我有下表:

+----------+------------------------+-----------+---------------------+
| PersonId |          Role          |  TeamId   |    EffectiveDate    |
+----------+------------------------+-----------+---------------------+
|  0001813 | admin                  | 005aba1ec | 2019-05-01 00:00:00 |
|  0001813 | scrum master           | 005aba1ec | 2019-05-01 00:00:00 |
|  0001813 | team captain           | 005aba1ec | 2019-05-01 00:00:00 |
|  0001813 | admin                  | 005aba1ec | 2019-06-01 00:00:00 |
|  0001813 | scrum master           | 005aba1ec | 2019-06-01 00:00:00 |
|  0001813 | team captain           | 005aba1ec | 2019-06-01 00:00:00 |
|  0001813 | delivery lead          | 005aba1ec | 2019-06-01 00:00:00 |
|  0002817 | product lead           | 007aba338 | 2019-07-01 00:00:00 |
|  0002817 | finance partner        | 007aba338 | 2019-07-01 00:00:00 |
|  0002817 | individual contributor | 007aba338 | 2019-07-01 00:00:00 |
|  0002817 | product lead           | 007aba338 | 2019-08-01 00:00:00 |
|  0002817 | finance partner        | 007aba338 | 2019-08-01 00:00:00 |
|  0002817 | individual contributor | 007aba338 | 2019-08-01 00:00:00 |
|  0002817 | admin                  | 007aba338 | 2019-08-01 00:00:00 |
+----------+------------------------+-----------+---------------------+

我想获得每行的下一个生效日期。对于具有相同生效日期的行,我想获取下一个更大的生效日期。本质上,我想获得以下结果:

+----------+------------------------+-----------+---------------------+---------------------+
| PersonId |          Role          |  TeamId   |    EffectiveDate    |  NextEffectiveDate  |
+----------+------------------------+-----------+---------------------+---------------------+
|  0001813 | admin                  | 005aba1ec | 2019-05-01 00:00:00 | 2019-06-01 00:00:00 |
|  0001813 | scrum master           | 005aba1ec | 2019-05-01 00:00:00 | 2019-06-01 00:00:00 |
|  0001813 | team captain           | 005aba1ec | 2019-05-01 00:00:00 | 2019-06-01 00:00:00 |
|  0001813 | admin                  | 005aba1ec | 2019-06-01 00:00:00 | 9999-12-31 23:59:59 |
|  0001813 | scrum master           | 005aba1ec | 2019-06-01 00:00:00 | 9999-12-31 23:59:59 |
|  0001813 | team captain           | 005aba1ec | 2019-06-01 00:00:00 | 9999-12-31 23:59:59 |
|  0001813 | delivery lead          | 005aba1ec | 2019-06-01 00:00:00 | 9999-12-31 23:59:59 |
|  0002817 | product lead           | 007aba338 | 2019-07-01 00:00:00 | 2019-08-01 00:00:00 |
|  0002817 | finance partner        | 007aba338 | 2019-07-01 00:00:00 | 2019-08-01 00:00:00 |
|  0002817 | individual contributor | 007aba338 | 2019-07-01 00:00:00 | 2019-08-01 00:00:00 |
|  0002817 | product lead           | 007aba338 | 2019-08-01 00:00:00 | 9999-12-31 23:59:59 |
|  0002817 | finance partner        | 007aba338 | 2019-08-01 00:00:00 | 9999-12-31 23:59:59 |
|  0002817 | individual contributor | 007aba338 | 2019-08-01 00:00:00 | 9999-12-31 23:59:59 |
|  0002817 | admin                  | 007aba338 | 2019-08-01 00:00:00 | 9999-12-31 23:59:59 |
+----------+------------------------+-----------+---------------------+---------------------+

我尝试在Postgres中使用LEAD函数,但我不认为PARTITION BY的工作方式与我认为的相同:

LEAD(EffectiveDate) OVER (PARTITION BY EffectiveDate ORDER BY EffectiveDate) AS NextEffectiveDate

关于如何实现这一目标的任何想法?预先感谢。

2 个答案:

答案 0 :(得分:1)

demo:db<>fiddle

SELECT 
    *,
    COALESCE (
        MAX("EffectiveDate") OVER 
             (PARTITION BY "PersonId" ORDER BY "EffectiveDate" GROUPS BETWEEN 1 FOLLOWING AND 1 FOLLOWING)
        ,
        '9999-12-31 23:59:59'
    )
FROM mytable

PostgreSQL 11 在窗口函数中添加了对GROUPS的支持。这正是它的目的。您希望始终获得EffectiveDate组的值。可以将其与PersonId分区结合使用,因为您只希望每个人都拥有GROUPS功能。 Further reading

然后,COALESCE()函数将最后一个GROUPS结果的NULL值重置为默认值。

答案 1 :(得分:1)

从Postgres 11开始,您还可以使用range定义的窗口函数来完成此操作:

MIN(EffectiveDate) OVER
    (PARTITION BY PersonId
     ORDER BY EffectiveDate
     RANGE BETWEEN INTERVAL '1 SECOND' FOLLOWING AND UNBOUNDED FOLLOWING
    ) AS NextEffectiveDate

在早期版本中,您可以使用两级窗口功能:

select t.*,
       nullif(max(next_id) over (partition by personid, effectivedate), effectivedate) as next_effectivedate
from (select t.*,
             lead(effectivedate) over (partition by personid order by effectivedate) as next_ed
      from t
     ) t