अपडेट करें मेरा मूल समाधान सही नहीं था। श्रेणियों के समेकन को नियमित विंडो में नियंत्रित नहीं किया जा सकता है। मैंने उसी नाम का उपयोग करके खुद को भ्रमित किया, trange
, यह भूलकर कि विंडो परिणाम पंक्तियों के बजाय स्रोत पंक्तियों के ऊपर है। कृपया अपडेट किया गया SQL Fiddle
देखें पूरी क्वेरी के साथ-साथ समस्या को स्पष्ट करने के लिए एक अतिरिक्त रिकॉर्ड के साथ।
प्रक्रिया के प्रत्येक चरण को दिखाने के लिए निम्नलिखित क्वेरी जानबूझकर वर्बोज़ है। कई चरणों को जोड़ा जा सकता है।
सबसे पहले, एक समावेशी [start, end]
जोड़ें प्रत्येक रिकॉर्ड की सीमा।
with add_ranges as (
select id, name, tsrange(start, "end", '[]') as t_range
from activities
),
id | name | t_range
----+------+-----------------------------------------------
1 | A | ["2018-01-09 17:00:00","2018-01-09 20:00:00"]
2 | A | ["2018-01-09 18:00:00","2018-01-09 20:30:00"]
3 | B | ["2018-01-09 19:00:00","2018-01-09 21:30:00"]
4 | B | ["2018-01-09 22:00:00","2018-01-09 23:00:00"]
(4 rows)
&&
. द्वारा निर्धारित ओवरलैपिंग श्रेणियों की पहचान करें ऑपरेटर और नए द्वीपों की शुरुआत को 1
. के साथ चिह्नित करें ।
mark_islands as (
select id, name, t_range,
case
when t_range && lag(t_range) over w then 0
else 1
end as new_range
from add_ranges
window w as (partition by name order by t_range)
),
id | name | t_range | new_range
----+------+-----------------------------------------------+-----------
1 | A | ["2018-01-09 17:00:00","2018-01-09 20:00:00"] | 1
2 | A | ["2018-01-09 18:00:00","2018-01-09 20:30:00"] | 0
3 | B | ["2018-01-09 19:00:00","2018-01-09 21:30:00"] | 1
4 | B | ["2018-01-09 22:00:00","2018-01-09 23:00:00"] | 1
(4 rows)
new_range
. के योग के आधार पर समूहों को क्रमांकित करें name
. के भीतर ।
group_nums as (
select id, name, t_range,
sum(new_range) over (partition by name order by t_range) as group_num
from mark_islands
),
id | name | t_range | group_num
----+------+-----------------------------------------------+-----------
1 | A | ["2018-01-09 17:00:00","2018-01-09 20:00:00"] | 1
2 | A | ["2018-01-09 18:00:00","2018-01-09 20:30:00"] | 1
3 | B | ["2018-01-09 19:00:00","2018-01-09 21:30:00"] | 1
4 | B | ["2018-01-09 22:00:00","2018-01-09 23:00:00"] | 2
name, group_num
. द्वारा समूहित करें द्वीप पर बिताए गए कुल समय के साथ-साथ संपूर्ण t_range
. प्राप्त करने के लिए ओवरलैप कटौती में इस्तेमाल किया जाना है।
islands as (
select name,
tsrange(min(lower(t_range)), max(upper(t_range)), '[]') as t_range,
max(upper(t_range)) - min(lower(t_range)) as island_time_interval
from group_nums
group by name, group_num
),
name | t_range | island_time_interval
------+-----------------------------------------------+----------------------
A | ["2018-01-09 17:00:00","2018-01-09 20:30:00"] | 03:30:00
B | ["2018-01-09 19:00:00","2018-01-09 21:30:00"] | 02:30:00
B | ["2018-01-09 22:00:00","2018-01-09 23:00:00"] | 01:00:00
(3 rows)
A
. के बीच ओवरलैप समय गिनने की आवश्यकता के लिए संदेश और B
संदेश, जब एक A
. की आवृत्तियां ढूंढें संदेश एक B
. को ओवरलैप करता है संदेश, और *
. का उपयोग करें चौराहा खोजने के लिए प्रतिच्छेदन संचालिका।
priority_overlaps as (
select b.name, a.t_range * b.t_range as overlap_range
from islands a
join islands b
on a.t_range && b.t_range
and a.name = 'A' and b.name != 'A'
),
name | overlap_range
------+-----------------------------------------------
B | ["2018-01-09 19:00:00","2018-01-09 20:30:00"]
(1 row)
प्रत्येक ओवरलैप के कुल समय को name
. से जोड़ दें ।
overlap_time as (
select name, sum(upper(overlap_range) - lower(overlap_range)) as total_overlap_interval
from priority_overlaps
group by name
),
name | total_overlap_interval
------+------------------------
B | 01:30:00
(1 row)
प्रत्येक name
. के लिए कुल समय की गणना करें ।
island_times as (
select name, sum(island_time_interval) as name_time_interval
from islands
group by name
)
name | name_time_interval
------+--------------------
B | 03:30:00
A | 03:30:00
(2 rows)
प्रत्येक name
. के लिए कुल समय में शामिल हों overlap_time
. से समायोजन के लिए सीटीई, और अंतिम duration
के लिए समायोजन घटाएं मूल्य।
select i.name,
i.name_time_interval - coalesce(o.total_overlap_interval, interval '0') as duration
from island_times i
left join overlap_time o
on o.name = i.name
;
name | duration
------+----------
B | 02:00:00
A | 03:30:00
(2 rows)