【发布时间】:2021-11-09 10:59:06
【问题描述】:
我在我们的 Postgresql 数据库中遇到了一个非常普遍的问题。许多表格包含仅在一段时间内有效的条目,例如可能随时间演变的合同的详细信息。
为了处理它,提供了valid from和valid to两个字段来表示行内容的有效期。每次合同更改时,都会在表格中添加一行,其中包含实际信息和相应的有效日期。
在对具有重叠有效性字段的表执行连接时,会出现主要问题。更准确地说,给定第一个表:
fg valid_from valid_to attr_table1
key1 2020-01-01 2020-01-18 A
key1 2020-01-19 null B
key2 2020-01-01 2020-01-30 A
key2 2020-01-30 null B
还有第二张桌子
fg valid_from valid_to attr_table2
key1 2020-01-01 2020-01-10 1.0
key1 2020-01-10 null 3.0
key2 2020-01-01 2020-01-30 10.0
key2 2020-01-30 null 11.0
我想建立一个联结表,其有效性字段嵌入两个表的有效期,如:
fg valid_from valid_to attr_table1 attr_table2
key1 2020-01-01 2020-01-10 A 1.0
key1 2020-01-10 2020-01-18 A 3.0
key1 2020-01-18 null B 3.0
key2 2020-01-01 2020-01-30 A 10.0
key2 2020-01-30 null B 11.0
到目前为止,我最有说服力的尝试是切换到 Postgresql 特定类型 daterange,并使用 && 运算符(“有共同点”)。我将valid from 和valid to 字段连接到validity 字段中,下一个查询似乎可以完成这项工作:
select t1.fg,
(case when upper(t1.validity) is null
then case when (upper(t2.validity) is null)
then case when lower(t1.validity) > lower(t2.validity)
then daterange(lower(t1.validity), null)
else daterange(lower(t2.validity), null)
end
else case when lower(t1.validity) > lower(t2.validity)
then daterange(lower(t1.validity), upper(t2.validity))
else daterange(lower(t2.validity), upper(t2.validity))
end
end
when upper(t2.validity) is null
then case when (upper(t1.validity) is null)
then case when lower(t1.validity) > lower(t2.validity)
then daterange(lower(t1.validity), null)
else daterange(lower(t2.validity), null)
end
else case when lower(t1.validity) > lower(t2.validity)
then daterange(lower(t1.validity), upper(t1.validity))
else daterange(lower(t2.validity), upper(t1.validity))
end
end
when lower(t1.validity) <= lower(t2.validity)
then case when upper(t1.validity) >= upper(t2.validity)
then daterange(lower(t2.validity), upper(t2.validity))
else daterange(lower(t2.validity), upper(t1.validity))
end
else case when upper(t1.validity) >= upper(t2.validity)
then daterange(lower(t1.validity), upper(t2.validity))
else daterange(lower(t1.validity), upper(t1.validity))
end
end
) as validity,
t1.attr_table1,
t2.attr_table2
from table1 as t1
join table2 as t2
on t1.fg = t2.fg
and t1.validity && t2.validity
order by fg, validity
但是,当第一个表的起点与第二个表中的任何条目都不匹配时,此查询将失败。例如,在第一个和第二个表中增加一行,例如
在表1中:
key1 2019-12-25 2020-01-01 A
在表2中:
key1 2019-12-27 2020-01-01 -1
输出表的第一行结果是
key1 2019-12-27 2020-01-01 A -1
而不是
key1 2019-12-25 2019-12-27 A null
key1 2019-12-27 2020-01-01 A -1
有人知道更好的方法吗?
编辑:创建table1和table2的代码,依赖于日期范围:
create table table1
(
fg text,
validity daterange,
attr_table1 text
);
insert into table1
values
('key1', daterange('2020-01-01', '2020-01-18'), 'A'),
('key1', daterange('2020-01-18', null ), 'B'),
('key2', daterange('2020-01-01', '2020-01-30'), 'A'),
('key2', daterange('2020-01-30', null ), 'B')
和
create table table2
(
fg text,
validity daterange,
attr_table2 text
);
insert into table2
values
('key1', daterange('2020-01-01', '2020-01-10'), 1.0),
('key1', daterange('2020-01-10', null ), 3.0),
('key2', daterange('2020-01-01', '2020-01-30'), 10.0),
('key2', daterange('2020-01-30', null ), 11.0)
【问题讨论】:
-
您能否发布创建表和插入或创建 sqlfiddle 以便我们可以处理您的数据?
-
你可以使用range_merge来简化你的case switch部分
-
@NuLo,你说得对,我使用 daterange 函数添加了创建表和插入。
-
每个键在每个表的第一行中是否具有相同的 valid_from 日期?比如key1在两个表中都是从2020-01-01开始的,但是table2中的第一行有没有可能是2020-01-02呢?
-
@MatBailie,是的,不同的键可以有不同的开始日期,同一个键在两个表中可以有不同的开始日期。这是我提出的方法的一个真正问题。 && 运算符无法按我的意愿合并第一行,如果开始日期不同..
标签: sql postgresql join date-range