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 相同,但仅限重叠 >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
如果您需要对“周期”进行大量计算,那么将它们直接存储为范围可能是一个好主意,这样可以在计算时节省大量来回转换。
| 归档时间: |
|
| 查看次数: |
1263 次 |
| 最近记录: |