【问题标题】:Get the previous row end date as the next row start date in POSTGRESQL在 POSTGRESQL 中获取上一行结束日期作为下一行开始日期
【发布时间】:2021-09-29 12:49:02
【问题描述】:

我在这里有一个表,其中包含每个员工的 hire_dateefficiency_date,每次都会更新。我想将上一行的生效日期作为下一行start_date,并且上一个日期的end_date将是负1以避免重叠。另外,我希望最后一条记录的 end_date 为 '2030-12-31' 作为默认值,Hire Date 作为 start_date第一个记录。请帮我解决这个问题。

当前表:

预期输出:

【问题讨论】:

  • 通过标签,我看到您已经确定了正确的窗口函数。你能和我们分享一下你到目前为止的尝试吗?

标签: postgresql lag lead


【解决方案1】:

我在您的原始表中发现了一些重复项,例如 RossChandler 有 2 个条目具有完全相同的数据集。如果那是复制/粘贴错误,那么我复制了您的案例:

create table testdt(name varchar, hire_dt date, eff_dt date);


insert into testdt values('Rachel','2021-03-29','2021-03-29');
insert into testdt values('Rachel','2021-03-29','2021-05-14');
insert into testdt values('Ross','2021-06-18','2021-06-18');
insert into testdt values('Monica','2021-07-21','2021-07-21');
insert into testdt values('Chandler','2021-04-12','2021-04-12');
insert into testdt values('Judy','2021-04-26','2021-04-26');
insert into testdt values('Judy','2021-04-26','2021-05-10');
insert into testdt values('Judy','2021-04-26','2021-07-01');

现在,正如您的标签所提到的那样,您需要使用 LAGLEAD 函数,这将相应地从上一行和下一行获取列。

如果您在上述testdt 表上运行以下SQL,您将获得上一个和下一个eff_dt。请注意我使用ctid 作为排序列

select *, 
    ctid, 
    lead(eff_dt) over (partition by name order by ctid) as lead_eff_dt,
    lag(eff_dt) over (partition by name order by ctid) as lag_eff_dt
from testdt tst order by ctid

结果如下

   name   |  hire_dt   |   eff_dt   | ctid  | lead_eff_dt | lag_eff_dt 
----------+------------+------------+-------+-------------+------------
 Rachel   | 2021-03-29 | 2021-03-29 | (0,1) | 2021-05-14  | 
 Rachel   | 2021-03-29 | 2021-05-14 | (0,2) |             | 2021-03-29
 Ross     | 2021-06-18 | 2021-06-18 | (0,3) |             | 
 Monica   | 2021-07-21 | 2021-07-21 | (0,4) |             | 
 Chandler | 2021-04-12 | 2021-04-12 | (0,5) |             | 
 Judy     | 2021-04-26 | 2021-04-26 | (0,6) | 2021-05-10  | 
 Judy     | 2021-04-26 | 2021-05-10 | (0,7) | 2021-07-01  | 2021-04-26
 Judy     | 2021-04-26 | 2021-07-01 | (0,8) |             | 2021-05-10

现在我们可以应用您的逻辑: START_DATE 如果是第一行(lag_eff_dtnull),那么我们使用hire_dt,否则,如果有下一行(lead_eff_dt 不是null),那么我们使用以下eff_dt。如果是最后一行,我们取eff_dt

END_DATE 如果没有后续记录(lead_eff_dtnull)我们只需要取2021-12-31,否则我们从下一个eff_dt 休息一天

下面的查询应该做

with first_sel as(
select *, 
    ctid, 
    lead(eff_dt) over (partition by name order by ctid) as lead_eff_dt,
    lag(eff_dt) over (partition by name order by ctid) as lag_eff_dt
from testdt tst order by ctid)
select 
    name, 
    case when lag_eff_dt is null
        then hire_dt
        else
            case when lead_eff_dt is not null
                then (lead_eff_dt)::date
                else eff_dt 
        end
        end as start,
    coalesce((lead_eff_dt - INTERVAL '1 DAY')::date, '2021-12-31'::date) as end
    

from first_sel;

结果是

   name   |   start    |    end     
----------+------------+------------
 Rachel   | 2021-03-29 | 2021-05-13
 Rachel   | 2021-05-14 | 2021-12-31
 Ross     | 2021-06-18 | 2021-12-31
 Monica   | 2021-07-21 | 2021-12-31
 Chandler | 2021-04-12 | 2021-12-31
 Judy     | 2021-04-26 | 2021-05-09
 Judy     | 2021-07-01 | 2021-06-30
 Judy     | 2021-07-01 | 2021-12-31
(8 rows)

【讨论】:

  • 哇!这很棒。另一件事,我想新列 supervisor,我该如何实现呢?我在 Select 中添加了主管,但是当它应该是最新的生效日期时,它会在分配给新主管时将 hire_date 作为 startDate。
  • 你需要提供一个具体的例子,否则我们无能为力
  • 这里:db-fiddle.com/f/4D8PbXMJR9tjFUiAVPZhDc/0Judy的第二条记录——开始日期不正确,应该是2021-05-10
猜你喜欢
  • 1970-01-01
  • 2019-10-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-04
相关资源
最近更新 更多