选择与另一个日期范围有 50% 重叠的日期范围

Spa*_*ger 1 postgresql plpgsql

我有一系列包含列的日期范围,例如 start_date、end_date、period_name

   1         100     Roman
 -50          75     Iron Age b
-100          20     Iron Age a
Run Code Online (Sandbox Code Playgroud)

我的团队要求我构建 2 个查询:

  1. 选择罗马时期和任何重叠的时期

  2. 与 1 相同,但仅限重叠 >40% 的范围

我假设我必须在 postgresql (PL/pgSQL) 中编写一个函数,非常欢迎任何帮助。

小智 5

这可以使用 Postgres 的范围类型很好地表达

第一个查询:查找与罗马时代重叠的所有时代:

select *
from ages a1
where period_name <> 'Roman'
  and exists (select *
              from ages a2
              where period_name = 'Roman' 
                and int4range(a1.start_date, a1.end_date) && int4range(a2.start_date, a2.end_date))
Run Code Online (Sandbox Code Playgroud)

对于第二个,需要计算重叠的长度,这可以使用范围的交集运算符*来完成: ,例如int4range(0,100,'[]') * int4range(-50,75,'[]')生成 76。必须将此结果与罗马时代的总长度进行比较才能获得百分比。

由于这需要同时访问两个时代(罗马时代和重叠时代),因此最好使用联接来完成。

为了使最终查询更易于阅读,我使用公用表表达式将两列转换为适当的范围,然后使用它进行连接。

一旦我有了那个。重叠百分比很容易计算:

with ranges as (
   select period_name, 
          int4range(start_date, end_date, '[]') as period,
          upper(int4range(start_date, end_date, '[]')) - lower(int4range(start_date, end_date, '[]')) as years
   from ages
), overlapping as (
  select o.period_name as overlapping_period_name, 
         r.period as roman_period, 
         o.period as overlapping_period, 
         r.period * o.period as overlap_length, 
         r.years as roman_years, 
         o.years as other_yers
  from ranges r
    join ranges o on r.period && o.period and o.period_name <> 'Roman'
  where r.period_name = 'Roman'
)
select overlapping_period_name, 
       overlapping_period,
       (upper(overlap_length) - lower(overlap_length))::numeric / roman_years as overlap_percent
from overlapping;
Run Code Online (Sandbox Code Playgroud)

需要强制转换::numeric,因为否则它将是整数除法,结果将0不是0.75

要仅显示重叠度至少为 40% 的内容,只需添加一个 where 条件:

.... 
select *
from overlapping
where (upper(overlap_length) - lower(overlap_length))::numeric / roman_years >= 0.4
Run Code Online (Sandbox Code Playgroud)

在线示例: http: //rextester.com/JIOAV33074


如果您需要对“周期”进行大量计算,那么将它们直接存储为范围可能是一个好主意,这样可以在计算时节省大量来回转换。