कई अलग-अलग विंडो फ़ंक्शंस और दो सबक्वेरी का उपयोग करते हुए, यह शालीनता से तेजी से काम करना चाहिए:
WITH events(id, event, ts) AS (
VALUES
(1, 12, '2014-03-19 08:00:00'::timestamp)
,(2, 12, '2014-03-19 08:30:00')
,(3, 13, '2014-03-19 09:00:00')
,(4, 13, '2014-03-19 09:30:00')
,(5, 12, '2014-03-19 10:00:00')
)
SELECT first_value(pre_id) OVER (PARTITION BY grp ORDER BY ts) AS pre_id
, id, ts
, first_value(post_id) OVER (PARTITION BY grp ORDER BY ts DESC) AS post_id
FROM (
SELECT *, count(step) OVER w AS grp
FROM (
SELECT id, ts
, NULLIF(lag(event) OVER w, event) AS step
, lag(id) OVER w AS pre_id
, lead(id) OVER w AS post_id
FROM events
WINDOW w AS (ORDER BY ts)
) sub1
WINDOW w AS (ORDER BY ts)
) sub2
ORDER BY ts;
ts
. का उपयोग करना टाइमस्टैम्प कॉलम के नाम के रूप में।
मान लें कि ts
अद्वितीय होना - और अनुक्रमित (एक अद्वितीय बाधा स्वचालित रूप से ऐसा करती है)।
50k पंक्तियों वाली वास्तविक जीवन तालिका वाले परीक्षण में इसे केवल एक एकल अनुक्रमणिका स्कैन . की आवश्यकता होती है . तो, बड़ी टेबल के साथ भी शालीनता से तेज होना चाहिए। इसकी तुलना में, शामिल / विशिष्ट के साथ आपकी क्वेरी एक मिनट (उम्मीद के अनुसार) के बाद समाप्त नहीं हुई।
यहां तक कि एक अनुकूलित संस्करण, एक समय में एक क्रॉस जॉइन से निपटना (बायां जुड़ाव मुश्किल से सीमित स्थिति के साथ प्रभावी रूप से सीमित है क्रॉस जॉइन) एक मिनट के बाद समाप्त नहीं हुआ।
बड़ी तालिका के साथ सर्वश्रेष्ठ प्रदर्शन के लिए, विशेष रूप से work_mem
के लिए अपनी मेमोरी सेटिंग्स को ट्यून करें। (बड़े प्रकार के संचालन के लिए)। अपने सत्र के लिए अस्थायी रूप से इसे (बहुत) उच्चतर सेट करने पर विचार करें यदि आप रैम को छोड़ सकते हैं। यहां और यहां और पढ़ें।
कैसे?
-
सबक्वेरी में
sub1
घटना को पिछली पंक्ति से देखें और इसे केवल तभी रखें जब यह बदल गया हो, इस प्रकार एक नए समूह के पहले तत्व को चिह्नित करना। साथ ही,id
प्राप्त करें पिछली और अगली पंक्ति की (pre_id
,post_id
)। -
सबक्वेरी में
sub2
,count()
केवल गैर-शून्य मानों की गणना करता है। परिणामीgrp
लगातार समान घटनाओं के ब्लॉक में साथियों को चिह्नित करता है। -
फाइनल में
SELECT
, पहलाpre_id
लें और अंतिमpost_id
वांछित परिणाम पर पहुंचने के लिए प्रत्येक पंक्ति के लिए प्रति समूह।
वास्तव में, यह बाहरीSELECT
में और भी तेज़ होना चाहिए :last_value(post_id) OVER (PARTITION BY grp ORDER BY ts RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS post_id
... चूंकि विंडो का क्रम क्रम
pre_id
. के लिए विंडो से सहमत है , इसलिए केवल एक ही प्रकार की आवश्यकता है। एक त्वरित परीक्षण इसकी पुष्टि करता प्रतीत होता है। इस फ़्रेम परिभाषा के बारे में अधिक जानकारी।
एसक्यूएल फिडल.