demo:db<>fiddle (ओवरलैपिंग ए-बी-पार्ट के साथ पुराने डेटा सेट का उपयोग करता है)
अस्वीकरण: यह दिन के अंतराल के लिए काम करता है न कि टाइमस्टैम्प के लिए। ts की आवश्यकता बाद में आई।
SELECT
s.acts,
s.sum,
MIN(a.start) as start,
MAX(a.end) as end
FROM (
SELECT DISTINCT ON (acts)
array_agg(name) as acts,
SUM(count)
FROM
activities, generate_series(start, "end", interval '1 day') gs
GROUP BY gs
HAVING cardinality(array_agg(name)) > 1
) s
JOIN activities a
ON a.name = ANY(s.acts)
GROUP BY s.acts, s.sum
generate_series
प्रारंभ और समाप्ति के बीच सभी तिथियां उत्पन्न करता है। इसलिए किसी गतिविधि के मौजूद होने की हर तारीख को विशिष्टcount
. के साथ एक पंक्ति मिलती है- सभी तिथियों को समूहीकृत करना, सभी मौजूदा गतिविधियों को एकत्रित करना और उनकी गणना का योग
HAVING
उन तिथियों को फ़िल्टर करता है जहां केवल एक गतिविधि मौजूद होती है- चूंकि समान गतिविधियों के साथ अलग-अलग दिन होते हैं, इसलिए हमें केवल एक प्रतिनिधि की आवश्यकता होती है:सभी डुप्लिकेट को
DISTINCT ON
के साथ फ़िल्टर करें - प्रारंभ और अंत प्राप्त करने के लिए मूल तालिका के विरुद्ध इस परिणाम में शामिल हों। (ध्यान दें कि पोस्टग्रेज में "एंड" एक आरक्षित शब्द है, आपको बेहतर तरीके से एक और कॉलम नाम खोजना चाहिए!) पहले उन्हें खोना अधिक सुविधाजनक था लेकिन इन डेटा को सबक्वायरी में प्राप्त करना संभव था।
- प्रत्येक अंतराल की सबसे प्रारंभिक और नवीनतम तिथि प्राप्त करने के लिए इस समूह में शामिल हों।
यहाँ टाइमस्टैम्प के लिए एक संस्करण है:
WITH timeslots AS (
SELECT * FROM (
SELECT
tsrange(timepoint, lead(timepoint) OVER (ORDER BY timepoint)),
lead(timepoint) OVER (ORDER BY timepoint) -- 2
FROM (
SELECT
unnest(ARRAY[start, "end"]) as timepoint -- 1
FROM
activities
ORDER BY timepoint
) s
)s WHERE lead IS NOT NULL -- 3
)
SELECT
GREATEST(MAX(start), lower(tsrange)), -- 6
LEAST(MIN("end"), upper(tsrange)),
array_agg(name), -- 5
sum(count)
FROM
timeslots t
JOIN activities a
ON t.tsrange && tsrange(a.start, a.end) -- 4
GROUP BY tsrange
HAVING cardinality(array_agg(name)) > 1
मुख्य विचार संभावित समय स्लॉट की पहचान करना है। इसलिए मैं हर ज्ञात समय (शुरुआत और अंत दोनों) लेता हूं और उन्हें एक क्रमबद्ध सूची में डाल देता हूं। तो मैं पहले टो ज्ञात समय (प्रारंभ ए से 17:00 और प्रारंभ बी से 18:00) ले सकता हूं और जांच सकता हूं कि इसमें कौन सा अंतराल है। फिर मैं इसे दूसरे और तीसरे के लिए देखता हूं, फिर तीसरे और चौथे के लिए और इसी तरह।
पहले समय में केवल ए फिट बैठता है। 18-19 से दूसरे में भी बी फिट है। अगले स्लॉट 19-20 में भी सी, 20 से 20:30 तक ए अब फिट नहीं है, केवल बी और सी। अगला वाला 20:30-22 है जहां केवल बी फिट बैठता है, अंत में 22-23 डी जोड़ा जाता है बी और अंतिम लेकिन कम से कम केवल डी 23-23:30 में फिट बैठता है।
इसलिए मैं इस समय सूची को लेता हूं और इसे फिर से गतिविधियों की तालिका में शामिल करता हूं जहां अंतराल प्रतिच्छेद करते हैं। उसके बाद यह केवल समय स्लॉट के आधार पर एक समूहीकरण है और अपनी गिनती का योग करें।
- यह एक पंक्ति के दोनों ts को एक सरणी में रखता है जिसके तत्वों को
unnest
के साथ प्रति तत्व एक पंक्ति में विस्तारित किया जाता है . इसलिए मैं हर समय एक कॉलम में आ जाता हूं जिसे आसानी से ऑर्डर किया जा सकता है - लीड का उपयोग करना विंडो फ़ंक्शन
अगली पंक्ति के मान को वर्तमान में ले जाने की अनुमति देता है। इसलिए मैं
tsrange
. के साथ इन दोनों मानों में से एक टाइमस्टैम्प श्रेणी बना सकता हूं - यह फ़िल्टर आवश्यक है क्योंकि अंतिम पंक्ति का कोई "अगला मान" नहीं है। यह एक
NULL
बनाता है मान जिसकी व्याख्याtsrange
. द्वारा की जाती है अनंत के रूप में। तो यह एक अविश्वसनीय गलत टाइम स्लॉट तैयार करेगा। इसलिए हमें इस पंक्ति को फ़िल्टर करना होगा। - मूल तालिका के सामने समय स्लॉट में शामिल हों।
&&
ऑपरेटर जाँचता है कि क्या दो श्रेणी प्रकार ओवरलैप करते हैं। - एकल समय स्लॉट के आधार पर समूह बनाना, नामों और गिनती को जोड़ना।
HAVING
. का उपयोग करके केवल एक गतिविधि वाले समय स्लॉट को फ़िल्टर करें खंड - सही शुरुआत और अंत अंक हासिल करना थोड़ा मुश्किल है। तो प्रारंभ बिंदु या तो अधिकतम गतिविधि प्रारंभ या एक समय स्लॉट की शुरुआत है (जिसे
lower
का उपयोग करके प्राप्त किया जा सकता है ) उदा. 20-20:30 स्लॉट लें:यह 20 घंटे से शुरू होता है लेकिन न तो बी और न ही सी का शुरुआती बिंदु है। समाप्ति समय समान।