अंतराल और द्वीप कार्य क्लासिक क्वेरी चुनौतियां हैं जहां आपको अनुक्रम में लापता मूल्यों की श्रेणियों और मौजूदा मूल्यों की श्रेणियों की पहचान करने की आवश्यकता होती है। अनुक्रम अक्सर कुछ दिनांक, या दिनांक और समय मानों पर आधारित होता है, जो सामान्य रूप से नियमित अंतराल में प्रकट होना चाहिए, लेकिन कुछ प्रविष्टियां गायब हैं। अंतराल कार्य लापता अवधि के लिए दिखता है और द्वीप कार्य मौजूदा अवधि के लिए दिखता है। मैंने अतीत में अपनी पुस्तकों और लेखों में अंतराल और द्वीपों के कार्यों के कई समाधानों को शामिल किया है। हाल ही में मुझे मेरे मित्र, एडम मचानिक द्वारा एक नई विशेष द्वीप चुनौती के साथ प्रस्तुत किया गया था, और इसे हल करने के लिए थोड़ी रचनात्मकता की आवश्यकता थी। इस लेख में मैं चुनौती और समाधान प्रस्तुत करता हूं जिसके साथ मैं आया था।
चुनौती
अपने डेटाबेस में आप कंपनी सर्विसेज नामक तालिका में आपकी कंपनी द्वारा समर्थित सेवाओं का ट्रैक रखते हैं, और प्रत्येक सेवा आम तौर पर एक मिनट में एक बार रिपोर्ट करती है कि यह इवेंटलॉग नामक तालिका में ऑनलाइन है। निम्न कोड इन तालिकाओं को बनाता है और उन्हें नमूना डेटा के छोटे सेटों से भर देता है:
NOCOUNT ON सेट करें; टेम्पर्ड का उपयोग करें; अगर OBJECT_ID(N'dbo.EventLog') नल ड्रॉप टेबल नहीं है dbo.EventLog; अगर OBJECT_ID(N'dbo.CompanyServices') पूरी तरह से ड्रॉप टेबल नहीं है dbo.CompanyServices; तालिका बनाएं dbo.CompanyServices ( serviceid INT NOT NULL, CONSTRAINT PK_CompanyServices PRIMARY KEY(serviceid)); dbo.CompanyServices(serviceid) Values(1), (2),(3) में इंसर्ट करें; क्रिएट टेबल dbo.EventLog (लॉगिड INT NOT NULL IDENTITY, serviceid INT NOT NULL, logtime DATETIME2(0) NOT NULL, CONSTRAINT PK_EventLog PRIMARY KEY(logid)); dbo.EventLog(serviceid, logtime) VALUES (1, '20180912 08:00:00'), (1, '20180912 08:01:01'), (1, '20180912 08:01:59') में प्रवेश करें। , (1, '20180912 08:03:00'), (1, '20180912 08:05:00'), (1, '20180912 08:06:02'), (2, '20180912 08:00:02 '), (2, '20180912 08:01:03'), (2, '20180912 08:02:01'), (2, '20180912 08:03:00'), (2, '20180912 08:03') :59'), (2, '20180912 08:05:01'), (2, '20180912 08:06:01'), (3, '20180912 08:00:01'), (3, '20180912 08') :03:01'), (3, '20180912 08:04:02'), (3, '20180912 08:06:00'); चुनें * dbo.EventLog से;
EventLog तालिका वर्तमान में निम्न डेटा से भरी हुई है:
लॉगिड सर्विसिड लॉगटाइम ----------- ------------------------------------- ----- 1 1 2018-09-12 08:00:00 2 1 2018-09-12 08:01:01 3 1 2018-09-12 08:01:59 4 1 2018-09-12 08:03:00 5 1 2018-09-12 08:05:00 6 1 2018-09-12 08:06:02 7 2 2018-09-12 08:00:02 8 2 2018-09-12 08:01:03 9 2 2018-09-12 08:02:01 10 2 2018-09-12 08:03:00 11 2 2018-09-12 08:03:59 12 2 2018-09-12 08:05:01 13 2 2018-09-12 08:06:01 14 3 2018-09-12 08:00:01 15 3 2018-09-12 08:03:01 16 3 2018-09-12 08:04:02 17 3 2018 -09-12 08:06:00
विशेष द्वीप कार्य उपलब्धता अवधि (सेवा, प्रारंभ समय, समाप्ति समय) की पहचान करना है। एक पकड़ यह है कि इस बात का कोई आश्वासन नहीं है कि कोई सेवा रिपोर्ट करेगी कि यह हर मिनट ऑनलाइन है; आपको पिछली लॉग प्रविष्टि से 66 सेकंड तक का अंतराल सहन करना चाहिए और फिर भी इसे उसी उपलब्धता अवधि (द्वीप) का हिस्सा मानना चाहिए। 66 सेकंड के बाद, नई लॉग प्रविष्टि एक नई उपलब्धता अवधि प्रारंभ करती है। तो, उपरोक्त इनपुट नमूना डेटा के लिए, आपके समाधान को निम्नलिखित परिणाम सेट वापस करना चाहिए (जरूरी नहीं कि इस क्रम में):
सर्विसिड स्टार्टटाइम एंडटाइम ----------- --------------------------- ------ --------------------- 1 2018-09-12 08:00:00 2018-09-12 08:03:00 1 2018-09-12 08:05:00 2018-09-12 08:06:02 2 2018-09-12 08:00:02 2018-09-12 08:06:01 3 2018-09-12 08:00:01 2018-09-12 08:00:01 3 2018-09-12 08:03:01 2018-09-12 08:04:02 3 2018-09-12 08:06:00 2018-09-12 08:06:00
ध्यान दें, उदाहरण के लिए, कैसे लॉग प्रविष्टि 5 एक नया द्वीप शुरू करती है क्योंकि पिछली लॉग प्रविष्टि से अंतराल 120 सेकंड (> 66) है, जबकि लॉग प्रविष्टि 6 एक नया द्वीप शुरू नहीं करता है क्योंकि पिछली प्रविष्टि से अंतराल 62 सेकंड है ( <=66)। एक और पकड़ यह है कि एडम चाहता था कि समाधान पूर्व-एसक्यूएल सर्वर 2012 के वातावरण के अनुकूल हो, जो इसे बहुत कठिन चुनौती बनाता है, क्योंकि आप चल रहे योग और ऑफसेट विंडो फ़ंक्शन की गणना करने के लिए एक फ्रेम के साथ विंडो एग्रीगेट फ़ंक्शन का उपयोग नहीं कर सकते हैं। LAG और LEAD की तरह। हमेशा की तरह, मेरा सुझाव है कि मेरे समाधानों को देखने से पहले स्वयं चुनौती को हल करने का प्रयास करें। अपने समाधानों की वैधता की जांच करने के लिए नमूना डेटा के छोटे सेट का उपयोग करें। नमूना डेटा के बड़े सेट के साथ अपनी तालिकाओं को पॉप्युलेट करने के लिए निम्न कोड का उपयोग करें (500 सेवाएं, ~10M लॉग प्रविष्टियां आपके समाधानों के प्रदर्शन का परीक्षण करने के लिए):
-- हेल्पर फ़ंक्शन dbo.GetNums IF OBJECT_ID(N'dbo.GetNums' ) नॉट न्यूल ड्रॉप फंक्शन dbo.GetNums; GO CREATE FUNCTION dbo.GetNums(@low as BIGINT, @high as BIGINT) L0 AS के साथ रिटर्न के रूप में रिटर्न टेबल (सेलेक्ट c (सेलेक्ट 1 यूनियन ऑल सेलेक्ट 1) AS D (c)), L1 AS (सेलेक्ट 1 AS c) एल0 से एक क्रॉस जॉइन एल0 एएस बी), एल2 एएस (एक क्रॉस जॉइन एल 1 एएस बी के रूप में एल 1 से सी चुनें), एल 3 एएस (एल 2 से एक क्रॉस जॉइन एल 2 एएस बी के रूप में चयन करें), एल 4 एएस ( L3 से C के रूप में 1 का चयन करें L3 AS B के रूप में, L5 AS (L4 से C के रूप में 1 चुनें C CROSS JOIN L4 AS B के रूप में), अंक AS (चयन ROW_NUMBER () ओवर (ऑर्डर बाय (चुनें NULL)) AS L5 से पंक्ति संख्या) शीर्ष चुनें(@high - @low + 1) @low + Rownum - 1 AS n FROM nums ORDER by rownum; GO -- ~ 10,000,000 अंतराल DECLARE @numservices AS INT =500, @logsperservice AS INT =20000, @enddate AS DATETIME2(0) ='20180912', @validinterval AS INT =60, -- सेकंड @normdifferential AS INT =3, -- सेकंड @percentmissing AS FLOAT =0.01; TRUNCATE TABLE dbo.EventLog; TRUNCATE TABLE dbo.CompanyServices; dbo.CompanyServices(serviceid) में सम्मिलित करें dbo.GetNums(1, @numservices) AS A से सर्विसिड के रूप में A.n चुनें; C AS के साथ (S.n AS serviceid चुनें, DATEADD(दूसरा, -L.n * @validinterval + CHECKSUM(NEWID())% (@normdifferential + 1), @enddate) AS logtime, RAND(CHECKSUM(NEWID())) AS rnd dbo.GetNums(1, @numservices) से एस क्रॉस जॉइन के रूप में dbo.GetNums(1, @logsperservice) AS L) dbo में डालें। शत-प्रतिशत;
मेरे समाधान के चरणों के लिए मैं जो आउटपुट प्रदान करूंगा, वे नमूना डेटा के छोटे सेटों को मानेंगे, और जो प्रदर्शन संख्याएं मैं प्रदान करूंगा, वे बड़े सेट मानेंगे।
मेरे द्वारा प्रस्तुत किए जाने वाले सभी समाधान निम्न अनुक्रमणिका से लाभान्वित होंगे:
Dbo.EventLog(serviceid, logtime, logid) पर INDEX idx_sid_ltm_lid बनाएं;
शुभकामनाएँ!
SQL सर्वर 2012+ के लिए समाधान 1
इससे पहले कि मैं एक ऐसे समाधान को कवर करूं जो पूर्व-एसक्यूएल सर्वर 2012 परिवेशों के अनुकूल हो, मैं एक ऐसे समाधान को कवर करूंगा जिसके लिए न्यूनतम SQL सर्वर 2012 की आवश्यकता होगी। मैं इसे समाधान 1 कहूंगा।
समाधान में पहला कदम isstart नामक ध्वज की गणना करना है जो कि 0 है यदि घटना एक नया द्वीप शुरू नहीं करती है, और 1 अन्यथा। यह पिछली घटना के लॉग समय को प्राप्त करने के लिए एलएजी फ़ंक्शन का उपयोग करके प्राप्त किया जा सकता है और जांच कर सकता है कि पिछली और वर्तमान घटनाओं के बीच सेकंड में समय अंतर अनुमत अंतराल से कम या बराबर है या नहीं। इस चरण को लागू करने वाला कोड यहां दिया गया है:
DECLARE @allowedgap AS INT =66; - सेकंड में चुनें *, केस जब दिनांकित (दूसरा, अंतराल (लॉगटाइम) ओवर (लॉगटाइम, लॉगिड द्वारा सर्विसिड ऑर्डर द्वारा विभाजन), लॉगटाइम) <=@allowedgap फिर 0 ईएलएसई 1 अंत के रूप में dbo.EventLog से शुरू होता है;
यह कोड निम्न आउटपुट उत्पन्न करता है:
लॉगिड सर्विसिड लॉगटाइम शुरू है ----------- -------------------------------- ------ ----------- 1 1 2018-09-12 08:00:00 1 2 1 2018-09-12 08:01:01 0 3 1 2018-09-12 08:01:59 0 4 1 2018-09-12 08:03:00 0 5 1 2018-09-12 08:05:00 1 6 1 2018-09-12 08:06:02 0 7 2 2018-09 -12 08:00:02 1 8 2 2018-09-12 08:01:03 0 9 2 2018-09-12 08:02:01 0 10 2 2018-09-12 08:03:00 0 11 2 2018 -09-12 08:03:59 0 12 2 2018-09-12 08:05:01 0 13 2 2018-09-12 08:06:01 0 14 3 2018-09-12 08:00:01 1 15 3 2018-09-12 08:03:01 1 16 3 2018-09-12 08:04:02 0 17 3 2018-09-12 08:06:00 1
इसके बाद, आईस्टार्ट ध्वज का एक साधारण चलने वाला कुल एक द्वीप पहचानकर्ता उत्पन्न करता है (मैं इसे जीआरपी कहूंगा)। इस चरण को लागू करने वाला कोड यहां दिया गया है:
DECLARE @allowedgap AS INT =66; C1 AS के साथ (चुनें *, मामला जब दिनांकित (दूसरा, अंतराल (लॉगटाइम) ओवर (लॉगटाइम, लॉगिड द्वारा सर्विसिड ऑर्डर द्वारा विभाजन), लॉगटाइम) <=@allowedgap फिर 0 ELSE 1 END AS isstart from dbo.EventLog ) चुनें *, SUM(isstart) OVER(सर्विसिड ऑर्डर द्वारा लॉगटाइम द्वारा विभाजन, लॉगिड ROWS अनबाउंडेड PRECEDING) C1 से जीआरपी के रूप में;
यह कोड निम्न आउटपुट उत्पन्न करता है:
लॉगिड सर्विसिड लॉगटाइम isstart grp ----------- ------------------------------------- ------- ------------ ----------- 1 1 2018-09-12 08:00:00 1 1 2 1 2018-09-12 08:01:01 0 1 3 1 2018-09-12 08:01:59 0 1 4 1 2018-09-12 08:03:00 0 1 5 1 2018-09-12 08:05:00 1 2 6 1 2018-09-12 08:06:02 0 2 7 2 2018-09-12 08:00:02 1 1 8 2 2018-09-12 08:01:03 0 1 9 2 2018-09-12 08:02:01 0 1 10 2 2018-09-12 08:03:00 0 1 11 2 2018-09-12 08:03:59 0 1 12 2 2018-09-12 08:05:01 0 1 13 2 2018 -09-12 08:06:01 0 1 14 3 2018-09-12 08:00:01 1 1 15 3 2018-09-12 08:03:01 1 2 16 3 2018-09-12 08:04:02 0 2 17 3 2018-09-12 08:06:00 1 3
अंत में, आप सेवा आईडी और द्वीप पहचानकर्ता द्वारा पंक्तियों को समूहित करते हैं और प्रत्येक द्वीप के प्रारंभ समय और समाप्ति समय के रूप में न्यूनतम और अधिकतम लॉग समय लौटाते हैं। ये रहा पूरा समाधान:
DECLARE @allowedgap AS INT =66; C1 AS के साथ (चुनें *, केस जब दिनांकित (दूसरा, अंतराल (लॉगटाइम) ओवर (लॉगटाइम, लॉगिड द्वारा सर्विसिड ऑर्डर द्वारा विभाजन), लॉगटाइम) <=@allowedgap तब 0 ELSE 1 END AS isstart from dbo.EventLog ), C2 AS (चुनें *, SUM (isstart) ओवर (लॉगटाइम द्वारा सर्विसिड ऑर्डर द्वारा पार्टिशन, लॉगिड रो अनबाउंडेड प्रीसीडिंग) C1 से जीआरपी के रूप में) सेलेक्ट सर्विसिड, मिन (लॉगटाइम) एएस स्टार्टटाइम, मैक्स (लॉगटाइम) एंडटाइम के रूप में सर्विसिड द्वारा सी 2 ग्रुप से एंडटाइम, जीआरपी;
इस समाधान को मेरे सिस्टम पर पूरा होने में 41 सेकंड का समय लगा, और चित्र 1 में दिखाया गया प्लान तैयार किया।
चित्र 1:समाधान 1 के लिए योजना
जैसा कि आप देख सकते हैं, दोनों विंडो फ़ंक्शंस की गणना इंडेक्स ऑर्डर के आधार पर की जाती है, बिना स्पष्ट सॉर्टिंग की आवश्यकता के।
यदि आप SQL सर्वर 2016 या बाद के संस्करण का उपयोग कर रहे हैं, तो आप एक खाली फ़िल्टर्ड कॉलमस्टोर इंडेक्स बनाकर बैच मोड विंडो एग्रीगेट ऑपरेटर को सक्षम करने के लिए यहां कवर की गई ट्रिक का उपयोग कर सकते हैं, जैसे:
dbo.EventLog(logid) पर नॉनक्लस्टर्ड कॉलमस्टोर इंडेक्स idx_cs बनाएं जहां लॉगिड =-1 और लॉगिड =-2;
वही समाधान अब मेरे सिस्टम पर पूरा होने में केवल 5 सेकंड लेता है, चित्र 2 में दिखाया गया प्लान तैयार करता है।
चित्र 2:बैच मोड विंडो एग्रीगेट ऑपरेटर का उपयोग करके समाधान 1 की योजना बनाएं
यह सब बहुत अच्छा है, लेकिन जैसा कि बताया गया है, एडम एक ऐसे समाधान की तलाश में था जो 2012 से पहले के वातावरण पर चल सके।
जारी रखने से पहले, सुनिश्चित करें कि आप सफाई के लिए कॉलमस्टोर इंडेक्स को छोड़ दें:
DROP INDEX idx_cs ON dbo.EventLog;
पूर्व-SQL सर्वर 2012 परिवेशों के लिए समाधान 2
दुर्भाग्य से, SQL सर्वर 2012 से पहले, हमारे पास LAG जैसे ऑफ़सेट विंडो फ़ंक्शंस के लिए समर्थन नहीं था, न ही हमारे पास फ़्रेम के साथ विंडो एग्रीगेट फ़ंक्शंस के साथ रनिंग टोटल की गणना के लिए समर्थन था। इसका मतलब है कि उचित समाधान के साथ आने के लिए आपको बहुत अधिक मेहनत करने की आवश्यकता होगी।
मैंने जिस ट्रिक का उपयोग किया है, वह प्रत्येक लॉग प्रविष्टि को एक कृत्रिम अंतराल में बदलना है जिसका प्रारंभ समय प्रविष्टि का लॉग समय है और जिसका अंतिम समय प्रविष्टि का लॉग समय और अनुमत अंतराल है। फिर आप कार्य को क्लासिक अंतराल पैकिंग कार्य के रूप में मान सकते हैं।
समाधान में पहला चरण कृत्रिम अंतराल सीमांकक की गणना करता है, और पंक्ति संख्या प्रत्येक घटना प्रकार (काउंटीच) की स्थिति को चिह्नित करता है। इस चरण को लागू करने वाला कोड यहां दिया गया है:
DECLARE @allowedgap AS INT =66; लॉगिड, सर्विसिड, लॉगटाइम एएस एस, - महत्वपूर्ण, 'एस'> 'ई' का चयन करें, बाद में ऑर्डर करने के लिए DATEADD (दूसरा, @allowedgap, लॉगटाइम) एएस ई, ROW_NUMBER() ओवर (सर्विसिड ऑर्डर द्वारा लॉगटाइम, लॉगिड द्वारा विभाजन) के रूप में dbo.EventLog से गिनती;
यह कोड निम्न आउटपुट उत्पन्न करता है:
logid serviceid s e counteach ----------------------------------------------------- ------------- ---------- 1 1 2018-09-12 08:00:00 2018-09-12 08:01:06 1 2 1 2018- 09-12 08:01:01 2018-09-12 08:02:07 2 3 1 2018-09-12 08:01:59 2018-09-12 08:03:05 3 4 1 2018-09-12 08 :03:00 2018-09-12 08:04:06 4 5 1 2018-09-12 08:05:00 2018-09-12 08:06:06 5 6 1 2018-09-12 08:06:02 2018-09-12 08:07:08 6 7 2 2018-09-12 08:00:02 2018-09-12 08:01:08 1 8 2 2018-09-12 08:01:03 2018-09- 12 08:02:09 2 9 2 2018-09-12 08:02:01 2018-09-12 08:03:07 3 10 2 2018-09-12 08:03:00 2018-09-12 08:04 :06 4 11 2 2018-09-12 08:03:59 2018-09-12 08:05:05 5 12 2 2018-09-12 08:05:01 2018-09-12 08:06:07 6 13 2 2018-09-12 08:06:01 2018-09-12 08:07:07 7 14 3 2018-09-12 08:00:01 2018-09- 12 08:01:07 1 15 3 2018-09-12 08:03:01 2018-09-12 08:04:07 2 16 3 2018-09-12 08:04:02 2018-09-12 08:05 :08 3 17 3 2018-09-12 08:06:00 2018-09-12 08:07:06 4
अगला कदम अंतराल को प्रारंभ और अंत घटनाओं के कालानुक्रमिक अनुक्रम में खोलना है, जिन्हें क्रमशः घटना प्रकार 'एस' और 'ई' के रूप में पहचाना जाता है। ध्यान दें कि s और e अक्षरों का चुनाव महत्वपूर्ण है ('s' > 'e'
) यह चरण दोनों प्रकार के ईवेंट के सही कालानुक्रमिक क्रम को चिह्नित करते हुए पंक्ति संख्याओं की गणना करता है, जो अब इंटरलीव्ड (काउंटबोथ) हैं। यदि एक अंतराल ठीक उसी स्थान पर समाप्त होता है जहां दूसरा शुरू होता है, तो अंत घटना से पहले प्रारंभ घटना की स्थिति बनाकर, आप उन्हें एक साथ पैक करेंगे। इस चरण को लागू करने वाला कोड यहां दिया गया है:
DECLARE @allowedgap AS INT =66; सी 1 एएस के साथ (सेलेक्ट लॉगिड, सर्विसिड, लॉगटाइम एएस एस, - महत्वपूर्ण, 'एस'> 'ई', बाद में ऑर्डर करने के लिए DATEADD (दूसरा, @allowedgap, लॉगटाइम) एएस ई, ROW_NUMBER() ओवर (सर्विसिड ऑर्डर द्वारा विभाजन) लॉगटाइम, लॉगिड) डीबीओ.इवेंटलॉग से काउंटच के रूप में) लॉगिड, सर्विसिड, लॉगटाइम, इवेंट टाइप, काउंटैच, ROW_NUMBER () ओवर (लॉगटाइम द्वारा सर्विसिड ऑर्डर द्वारा पार्टिशन, इवेंट टाइप डीईएससी, लॉगिड) सी 1 यूएनपीवीओटी (इवेंटटाइप के लिए लॉगटाइम) से काउंटबोथ के रूप में चुनें ( एस, ई)) एएस यू;
यह कोड निम्न आउटपुट उत्पन्न करता है:
लॉगिड सर्विसिड लॉगटाइम इवेंटटाइप काउंटईच काउंटबोथ ------------------------------------ ----- ---------- ---------- 1 1 2018-09-12 08:00:00 एस 1 1 2 1 2018-09-12 08:01 :01 एस 2 2 1 1 2018-09-12 08:01:06 ई 1 3 3 1 2018-09-12 08:01:59 एस 3 4 2 1 2018-09-12 08:02:07 ई 2 5 4 1 2018-09-12 08:03:00 एस 4 6 3 1 2018-09-12 08:03:05 ई 3 7 4 1 2018-09-12 08:04:06 ई 4 8 5 1 2018-09 -12 08:05:00 एस 5 9 6 1 2018-09-12 08:06:02 एस 6 10 5 1 2018-09-12 08:06:06 ई 5 11 6 1 2018-09-12 08:07 :08 ई 6 12 7 2 2018-09-12 08:00:02 एस 1 1 8 2 2018-09-12 08:01:03 एस 2 2 7 2 2018-09-12 08:01:08 ई 1 3 9 2 2018-09-12 08:02:01 एस 3 4 8 2 2018-09-12 08:02:09 ई 2 5 10 2 2018-09-12 08:03:00 एस 4 6 9 2 2018-09-12 08:03:07 ई 3 7 11 2 2018-09-12 08:03:59 एस 5 8 10 2 2018-09-12 08:04:06 ई 4 9 12 2 2018-09-12 08:05:01 एस 6 10 11 2 2018-09-12 08:05:05 ई 5 11 13 2 2018-09-12 08:06:01 एस 7 12 12 2 2018-09-12 08:06:07 ई 6 13 13 2 2018-09-12 08:07:07 ई 7 14 14 3 2018-09-12 08:00:01 एस 1 1 14 3 2018-09-12 08:01:07 ई 1 2 15 3 2018-09-12 08:03:01 एस 2 3 16 3 2018-09-12 08:04:02 एस 3 4 15 3 2018-09-12 08:04:07 ई 2 5 16 3 2018-09-12 08:05:08 ई 3 6 17 3 2018-09-12 08:06:00 एस 4 7 17 3 2018-09-12 08:07:06 ई 4 8
जैसा कि उल्लेख किया गया है, काउंटच केवल एक ही तरह की घटनाओं के बीच घटना की स्थिति को चिह्नित करता है, और काउंटबोथ घटना की स्थिति को संयुक्त, इंटरलीव्ड, दोनों प्रकार की घटनाओं के बीच चिह्नित करता है।
जादू को फिर अगले चरण द्वारा नियंत्रित किया जाता है - प्रत्येक घटना के बाद सक्रिय अंतराल की गणना की गणना करना, जो कि काउंटैच और काउंटबोथ पर आधारित है। सक्रिय अंतरालों की संख्या अब तक हुई प्रारंभ घटनाओं की संख्या को घटाकर अब तक हुई अंतिम घटनाओं की संख्या है। प्रारंभ की घटनाओं के लिए, काउंटच आपको बताता है कि अब तक कितने प्रारंभ कार्यक्रम हुए हैं, और आप यह पता लगा सकते हैं कि काउंटबोथ से काउंटैक घटाकर अब तक कितने समाप्त हुए हैं। तो, पूर्ण अभिव्यक्ति आपको बता रही है कि कितने अंतराल सक्रिय हैं:
काउंटीच - (काउंटबोथ - काउंटीच)
अंतिम घटनाओं के लिए, काउंटैच आपको बताता है कि अब तक कितनी अंतिम घटनाएं हुईं, और आप यह पता लगा सकते हैं कि काउंटबोथ से काउंटैक घटाकर अब तक कितने शुरू हुए हैं। तो, पूर्ण अभिव्यक्ति आपको बता रही है कि कितने अंतराल सक्रिय हैं:
(काउंटबोथ - काउंटैच) - काउंटीच
निम्नलिखित CASE व्यंजक का उपयोग करते हुए, आप घटना प्रकार के आधार पर गणनात्मक स्तंभ की गणना करते हैं:
केस जब इवेंट टाइप ='एस' फिर काउंटच - (काउंटबोथ - काउंटच) जब इवेंट टाइप ='ई' फिर (काउंटबोथ - काउंटैच) - काउंटैच ENDउसी चरण में आप केवल पैक किए गए अंतराल के प्रारंभ और अंत का प्रतिनिधित्व करने वाली घटनाओं को फ़िल्टर करते हैं। पैक्ड अंतरालों के प्रारंभ में एक प्रकार 's' और एक काउंटएक्टिव 1 होता है। पैक्ड अंतरालों के अंत में एक प्रकार 'e' और एक काउंटएक्टिव 0 होता है।
फ़िल्टर करने के बाद, आपके पास पैक्ड अंतराल के स्टार्ट-एंड इवेंट के जोड़े रह जाते हैं, लेकिन प्रत्येक जोड़ी दो पंक्तियों में विभाजित हो जाती है—एक स्टार्ट इवेंट के लिए और दूसरी एंड इवेंट के लिए। इसलिए, एक ही चरण सूत्र (rownum – 1) / 2 + 1 के साथ पंक्ति संख्याओं का उपयोग करके जोड़ी पहचानकर्ता की गणना करता है।
इस चरण को लागू करने वाला कोड यहां दिया गया है:
DECLARE @allowedgap AS INT =66; सी 1 एएस के साथ (सेलेक्ट लॉगिड, सर्विसिड, लॉगटाइम एएस एस, - महत्वपूर्ण, 'एस'> 'ई', बाद में ऑर्डर करने के लिए DATEADD (दूसरा, @allowedgap, लॉगटाइम) एएस ई, ROW_NUMBER() ओवर (सर्विसिड ऑर्डर द्वारा विभाजन) logtime, logid) dbo.EventLog से काउंटच के रूप में), C2 AS (सेलेक्ट लॉगिड, सर्विसिड, लॉगटाइम, इवेंट टाइप, काउंटैच, ROW_NUMBER () ओवर (लॉगटाइम द्वारा सर्विसिड ऑर्डर द्वारा पार्टिशन, इवेंट टाइप डीईएससी, लॉगिड) एएस काउंटबोथ फ्रॉम C1 UNPIVOT (लॉगटाइम) इवेंट टाइप इन (एस, ई)) एएस यू के लिए) सर्विसिड, इवेंट टाइप, लॉगटाइम, (ROW_NUMBER() ओवर (लॉगटाइम द्वारा सर्विसिड ऑर्डर द्वारा पार्टिशन, इवेंट टाइप डीईएससी, लॉगिड) - 1) / 2 + 1 एएस जीआरपी सी 2 क्रॉस से लागू करें। (मान (मामला जब घटना प्रकार ='एस' तब गिनती - (काउंटबोथ - काउंटच) जब घटना प्रकार ='ई' तब (काउंटबोथ - काउंटैच) - काउंटैच अंत)) ए (काउंटएक्टिव) जहां (ईवेंट टाइप ='एस' और काउंटएक्टिव =1) या (सम .) ttype ='e' और काउंटएक्टिव =0);यह कोड निम्न आउटपुट उत्पन्न करता है:
सर्विसिड इवेंट टाइप लॉगटाइम जीआरपी ----------- ------------------------------ - 1 एस 2018-09-12 08:00:00 1 1 ई 2018-09-12 08:04:06 1 1 एस 2018-09-12 08:05:00 2 1 ई 2018-09-12 08:07:08 2 2 एस 2018-09-12 08:00:02 1 2 ई 2018-09-12 08:07:07 1 3 एस 2018-09-12 08:00:01 1 3 ई 2018-09-12 08:01:07 1 3 एस 2018-09-12 08:03:01 2 3 ई 2018-09-12 08:05:08 2 3 एस 2018-09-12 08:06:00 3 3 ई 2018-09 -12 08:07:06 3अंतिम चरण घटनाओं के जोड़े को प्रति अंतराल एक पंक्ति में पिवट करता है, और सही घटना समय को पुन:उत्पन्न करने के लिए समाप्ति समय से अनुमत अंतराल को घटाता है। यहाँ संपूर्ण समाधान का कोड है:
DECLARE @allowedgap AS INT =66; सी 1 एएस के साथ (सेलेक्ट लॉगिड, सर्विसिड, लॉगटाइम एएस एस, - महत्वपूर्ण, 'एस'> 'ई', बाद में ऑर्डर करने के लिए DATEADD (दूसरा, @allowedgap, लॉगटाइम) एएस ई, ROW_NUMBER() ओवर (सर्विसिड ऑर्डर द्वारा विभाजन) logtime, logid) dbo.EventLog से काउंटच के रूप में), C2 AS (सेलेक्ट लॉगिड, सर्विसिड, लॉगटाइम, इवेंट टाइप, काउंटैच, ROW_NUMBER () ओवर (लॉगटाइम द्वारा सर्विसिड ऑर्डर द्वारा पार्टिशन, इवेंट टाइप डीईएससी, लॉगिड) एएस काउंटबोथ फ्रॉम C1 UNPIVOT (लॉगटाइम) इवेंट टाइप इन (एस, ई)) एएस यू के लिए), सी 3 एएस (सेलेक्ट सर्विसिड, इवेंट टाइप, लॉगटाइम, (आरओडब्ल्यू_NUMBER() ओवर (लॉगटाइम द्वारा सर्विसिड ऑर्डर द्वारा पार्टिशन, इवेंट टाइप डीईएससी, लॉगिड) - 1) / 2 + 1 एएस जीआरपी C2 क्रॉस से लागू (मान (मामला जब घटना प्रकार ='एस' तब गिनती - (काउंटबोथ - काउंटच) जब घटना प्रकार ='ई' तब (काउंटबोथ - काउंटैच) - काउंटैच अंत)) ए (काउंटएक्टिव) जहां (ईवेंट टाइप ='एस) ' और काउंटएक्टिव =1) या (ईवेंट टाइप ='ई' और काउंटएक्टिव =0)) सेलेक्ट सर्विसिड, एस एएस स्टार्टटाइम, DATEADD (दूसरा, -@allowedgap, ई) C3 PIVOT से एंडटाइम के रूप में (MAX (लॉगटाइम) इवेंट टाइप IN (s) के लिए, ई)) एएस पी;इस समाधान को मेरे सिस्टम पर पूरा होने में 43 सेकंड का समय लगा और चित्र 3 में दिखाया गया प्लान तैयार किया।
चित्र 3:समाधान 2 के लिए योजना
जैसा कि आप देख सकते हैं, पहली पंक्ति संख्या गणना की गणना सूचकांक क्रम के आधार पर की जाती है, लेकिन अगले दो में स्पष्ट छँटाई शामिल है। फिर भी, प्रदर्शन इतना बुरा नहीं है, क्योंकि इसमें लगभग 10,000,000 पंक्तियाँ शामिल हैं।
भले ही इस समाधान के बारे में एक पूर्व-एसक्यूएल सर्वर 2012 वातावरण का उपयोग करना है, केवल मनोरंजन के लिए, मैंने एक फ़िल्टर्ड कॉलमस्टोर इंडेक्स बनाने के बाद इसके प्रदर्शन का परीक्षण किया, यह देखने के लिए कि बैच प्रोसेसिंग सक्षम होने के साथ यह कैसे करता है:
dbo.EventLog(logid) पर नॉनक्लस्टर्ड कॉलमस्टोर इंडेक्स idx_cs बनाएं जहां लॉगिड =-1 और लॉगिड =-2;बैच प्रोसेसिंग सक्षम होने के साथ, इस समाधान को मेरे सिस्टम पर समाप्त होने में 29 सेकंड का समय लगा, चित्र 4 में दिखाया गया प्लान तैयार किया।
निष्कर्ष
यह स्वाभाविक है कि आपका वातावरण जितना सीमित होगा, क्वेरी कार्यों को हल करना उतना ही चुनौतीपूर्ण होगा। एडम की विशेष द्वीप चुनौती पुराने वाले की तुलना में SQL सर्वर के नए संस्करणों पर हल करना बहुत आसान है। लेकिन फिर आप अपने आप को और अधिक रचनात्मक तकनीकों का उपयोग करने के लिए मजबूर करते हैं। तो एक अभ्यास के रूप में, अपने पूछताछ कौशल में सुधार करने के लिए, आप उन चुनौतियों से निपट सकते हैं जिनसे आप पहले से परिचित हैं, लेकिन जानबूझकर कुछ प्रतिबंध लगाते हैं। आप कभी नहीं जानते कि आप किस तरह के दिलचस्प विचारों में ठोकर खा सकते हैं!