使用 postgresql,如何获得反映重叠时间段的日期值

Using postgresql, how can I get a date value reflecting overlapping period

我想使用 PostgreSQL 9.6 处理药物处方数据。

示例数据结构如下。

create table drug_table (
id int,
start_date date,
end_date date,
dose int
);

insert into drug_table values(1005, '2010-01-08', '2010-02-05', 15);
insert into drug_table values(1005, '2010-01-30', '2010-02-28', 10);
insert into drug_table values(1005, '2010-03-02', '2010-03-10', 20);
insert into drug_table values(1005, '2010-03-12', '2010-04-28', 20);
insert into drug_table values(1005, '2010-04-25', '2010-05-15', 20);

作为样本日期,每行之间有重叠期。在第一行和第二行之间, [2010-02-05 ~ 2010-01-30] 的时间段重叠。

当出现重叠时段时,后一行的开始和结束日期应延迟(在这种情况下,第二行的开始日期为“2010-02-05”,开始日期为“2010-03-06”考虑到第一行和第二行之间重叠 6 天的结束日期)。

我使用 window 函数尝试了这个问题。

select id,
    GREATEST(start_date, MAX(end_date) OVER (PARTITION BY id ORDER BY id, end_date ROWS BETWEEN UNBOUNDED PRECEDING AND 1 preceding)) re_start_date, 
    (GREATEST(start_date, MAX(end_date) OVER (PARTITION BY id ORDER BY id, end_date ROWS BETWEEN UNBOUNDED PRECEDING AND 1 preceding)) + interval '1' day *(end_date-start_date))::date re_end_date,
    dose
from drug_table
order by id, start_date, end_date;

此 sql 代码的结果如下。

id  re_start_date  re_end_date  dose
1005  2010-01-08  2010-02-05  15
1005  2010-02-05  *2010-03-06*  10
1005  *2010-03-02*  2010-03-10  20
1005  2010-03-12  2010-04-28  20
1005  2010-04-28  2010-05-18  20

但是,如果由于第 1 行和第 2 行之间的重叠而导致第 2 行的更新日期在第 2 和第 3 个处方之间发生重叠,则第 3 行不会反映此代码中第 2 行的更新日期。第 2 行和第 3 行之间有 [2010-03-06 ~ 2010-03-02] 的重叠期。我想将第 3 行设为开始日期的“2010-03-06”和结束日期的“2010-03-14”,以反映第 2 行的更新数据。

这个table就是我想要的结果

id  re_start_date  re_end_date  dose
1005  2010-01-08  2010-02-05  15
1005  2010-02-05  2010-03-06  10
1005  2010-03-06  2010-03-14  20
1005  2010-03-14  2010-04-30  20
1005  2010-04-30  2010-05-18  20

考虑到后续行的延迟日期,第 4、5 行也被延迟。

如果我再次使用window函数,它可以反映重叠仅存在两次时的延迟。但是当重叠更多时(三倍或更多...),我认为重用 window 函数不是好的解决方案。我的目的可能需要循环功能。

我能得到一些解决这个问题的提示吗? 如果可能的话,我想使用 Postgresql.

处理这些数据

我想你可以用一个函数来完成这个:

CREATE OR REPLACE FUNCTION shift_dates() RETURNS setof drug_table AS
$BODY$
DECLARE
  rw drug_table%rowtype;
  last_date date;
  shift_days integer;
  last_id integer;
BEGIN

  last_id = -314159;

  for rw in select * from drug_table order by id, start_date loop

    if rw.id != last_id then
      last_date := '0001-01-01';
      last_id := rw.id;
    end if;

    if rw.start_date < last_date then
      shift_days := last_date - rw.start_date;
      rw.start_date := last_date;
      rw.end_date := rw.end_date + shift_days;
    end if;

    last_date := rw.end_date;

    return next rw;
  end loop;

  return;

END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

当然还有:

select * from shift_dates();

我在您的数据上进行了测试,最后的日期不一致 -- 我想出了 5/30 而不是 5/28,但我相信有两天的轮班。换句话说,我认为5/30是对的。检查一下,看看你是否同意:

id      start_date  end_date    dose
1005    1/8/10      2/5/10      15
1005    2/5/10      3/6/10      10
1005    3/6/10      3/14/10     20
1005    3/14/10     4/30/10     20
1005    4/30/10     5/20/10     20

此外,根据您的示例数据,last_id 变量不是必需的,但假设您想针对一系列 id 值执行此操作,那么这(我希望)回答您的问题下一个问题。