使用 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
值执行此操作,那么这(我希望)回答您的问题下一个问题。
我想使用 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
值执行此操作,那么这(我希望)回答您的问题下一个问题。