2009-04-23 9 views
8

मैं एक बड़ी समस्या पर सड़क ब्लॉक में भाग रहा हूं।समय सीमा ओवरलैप की जांच, पहरेदार समस्या [एसक्यूएल]

एक बड़ी क्वेरी के हिस्से के रूप में मुझे "रात्रि घड़ी" समस्या को हल करने की आवश्यकता है। मैं इस तरह के रूप अनुसूची बदलाव के साथ एक मेज है:

ID | Start   | End 
1 | 2009-1-1 06:00 | 2009-1-1 14:00 
2 | 2009-1-1 10:00 | 2009-1-1 18:00 
3 | 2009-2-1 20:00 | 2009-2-2 04:00 
4 | 2009-2-2 06:00 | 2009-2-2 14:00 

एक प्रश्न के हिस्से के रूप में, मैं अगर वहाँ कम से कम 1 एक निश्चित समय सीमा के लिए हर समय एक कमरे में चौकीदार निर्धारित करने के लिए की जरूरत है।

इसलिए यदि मैंने 2009-1-1 06:00 से 2009-1-1 12:00 तक सीमा निर्दिष्ट की है, तो परिणाम सही है, क्योंकि इस समय अवधि को कवर करने के लिए 1 और 2 विलय बदल जाता है - वास्तव में घड़ी को बनाए रखने के लिए किसी भी प्रकार की शिफ्ट को जंजीर बनाया जा सकता है। हालांकि अगर मैंने 2009-2-1 22:00 से 2009-1-2 10:00 पर चेक किया है, तो परिणाम झूठा है क्योंकि अगली सुबह 4 और 6 बजे के बीच ब्रेक होता है।

मैं इस या तो LINQ में, या SQL सर्वर में एक उपयोगकर्ता परिभाषित समारोह (2005) के रूप में, लागू करने के लिए के रूप में दोनों ही मामलों में यह सिर्फ एक बड़ा प्रश्न के तर्क यह है कि करने के लिए चलाए किया जाना चाहिए का एक हिस्सा है चाहते हैं उन तत्वों की पहचान करें जिन्हें ध्यान देने की आवश्यकता है। वास्तविक डेटासेट में किसी भी समय अवधि को छेड़छाड़ करने वाले सौ शिफ्ट रिकॉर्ड शामिल होते हैं, लेकिन हमेशा पूरी श्रृंखला को कवर नहीं करते हैं।

मुझे सबसे नज़दीकी मिला है How to group ranged values using SQL Server संख्या सीमाओं के लिए, हालांकि यह अगली रेंज शुरू होने से ठीक पहले प्रत्येक श्रेणी पर निर्भर करता है। यदि मैं घड़ियों के समान एकीकृत दृश्य का निर्माण कर सकता हूं, तो केवल ओवरलैपिंग घड़ियों को ध्यान में रखकर, यह जांचना मुश्किल होगा कि कोई विशिष्ट समय कवर किया गया था या नहीं। एक एकीकृत दृश्य इस प्रकार दिखाई देगा:

Start   | End 
2009-1-1 06:00 | 2009-1-1 18:00 
2009-2-1 20:00 | 2009-2-2 04:00 
2009-2-2 06:00 | 2009-2-2 14:00 

नोट: यह पूरी बात अपेक्षाकृत सिर्फ सभी डेटा खींच रहा है और उस पर कुछ मार्गदर्शन पाश चलाकर लागू करने के लिए आसान होगा, मौजूदा प्रणाली है, और इसके बजाय तथापि कि बदलावों की संख्या और समय सीमा की संख्या के कारण धीमा होना चाहिए।

+0

"दिनांक अगर मैंने 200-1-112:00 से 200 9 -1-1 06:00 तक सीमा निर्दिष्ट की है, तो " 200-1-1 06:00 को उलट दिया जाना चाहिए 2009-1-1 12:00 "? – Sung

+2

@ डेविड आप इस ईबुक को डाउनलोड करना चाहेंगे: "एसक्यूएल में टाइम-ओरिएंटेड डाटाबेस एप्लीकेशन डेवलपमेंट" (http://www.cs.arizona.edu/people/rts/tdbbook.pdf)। तारीख सीमाओं के साथ तालिकाओं पर जटिल SQL क्वेरी के बारे में इसकी बहुत अच्छी जानकारी है। –

+0

+1 - मैं एक समान समस्या पर काम कर रहा हूं, विवादों की पहचान कर रहा हूं और – spencer7593

उत्तर

2

यहाँ इस

Start   | End 
2009-1-1 06:00 | 2009-1-1 18:00 
2009-2-1 20:00 | 2009-2-2 04:00 
2009-2-2 06:00 | 2009-2-2 14:00 

आप पिछलेऔरअगले प्रत्येक पंक्ति में तिथियों की तुलना करें और देखने के लिए की तरह तिथि सीमा समतल करने का एक तरीका है कि क्या

  • वर्तमान पंक्ति के की शुरुआत करें पिछली पंक्ति की दिनांक सीमा के बीच की तारीखें।
  • वर्तमान पंक्ति अंत दिनांक अगली पंक्ति की तारीख सीमा के बीच आता है।

alt text

कोड ऊपर का उपयोग करना, यूडीएफ को लागू करने के रूप में सरल रूप में पालन किया है।

create function fnThereIsWatchmenBetween(@from datetime, @to datetime) 
returns bit 
as 
begin 
    declare @_Result bit 

    declare @FlattenedDateRange table (
     Start datetime, 
     [End] datetime 
    ) 

    insert @FlattenedDateRange(Start, [End]) 
    select distinct 
      Start = 
       case 
        when Pv.Start is null then Curr.Start 
        when Curr.Start between Pv.Start and Pv.[End] then Pv.Start 
        else Curr.Start 
       end, 
      [End] = 
       case 
        when Curr.[End] between Nx.Start and Nx.[End] then Nx.[End] 
        else Curr.[End] 
       end 
    from shift Curr 
      left join shift Pv on Pv.ID = Curr.ID - 1 --; prev 
      left join shift Nx on Nx.ID = Curr.ID + 1 --; next 

    if exists( select 1 
       from FlattenedDateRange R 
       where @from between R.Start and R.[End] 
         and @to between R.Start and R.[End]) begin 
     set @_Result = 1 --; There is/are watchman/men during specified date range 
    end 
    else begin 
     set @_Result = 0 --; There is NO watchman 
    end 

    return @_Result 
end 
+0

उदाहरण के लिए, हमारे पास बदलाव हैं: 12-2 और 4-6 और हम 1-6 की जांच करते हैं। यह असफल होना चाहिए, क्योंकि 2-4 खाली है। – nlucaroni

+0

कोड अद्यतन किया गया है। – Sung

+0

बहुत अच्छा है।विडंबना यह है कि, यह एक बड़ी, अधिक जटिल समस्या का हिस्सा था, इसलिए मुझे ट्रिगर्स द्वारा संचालित लुकअप टेबल का एक प्रकार बनाकर पूरी चीज करने का एक तेज़ तरीका मिला, जिससे रिकॉर्ड किए जाने पर सीपीयू लागत का बड़ा हिस्सा बढ़ रहा था (जो तब बहुत कम होता है जब उन्हें पढ़ने और विश्लेषण करने की आवश्यकता होती है)। हालांकि मुझे इस तरह के ओवरलैपिंग श्रेणियों से निपटने के लिए बहुत कुछ करना है, इसलिए यह जल्द ही बहुत उपयोगी होगा। – David

1

एक अनगिनत अंतराल स्पष्ट रूप से देखे गए समय के अंत में या पूरे समय सीमा की शुरुआत में शुरू होता है जो आप जांच रहे हैं। इसलिए आपको एक ऐसी क्वेरी की आवश्यकता है जो इस सेट के सभी तत्वों का चयन करे जिनके पास ओवरलैपिंग शिफ्ट नहीं है। क्वेरी देखने की तरह होगा:

select 1 
from shifts s1 where not exists 
    (select 1 from shifts s2 
    where s2.start<=s1.end and s2.end > s1.end 
    ) 
    and s1.end>=start_of_range and s1.end< end_of_range 
union 
select 1 
where not exists 
    (select 1 from shifts s2 
     where s2.start<=start_of_range and s2.end > start_of_range 
    ) 

यदि यह गैर खाली है, तो आप एक निगरानी रहित अंतराल है। मुझे संदेह है कि यह वर्गबद्ध समय में चलेगा, इसलिए यह "सॉर्ट, फ़ेच और लूप" से धीमा हो सकता है।

+0

ओवरलैप कर रहा हूं मुझे इस काम को करने में कुछ परेशानी हो रही है। मुझे लगता है कि आप इसके लिए अंत समय के लिए कहीं भी फिल्टर करने का इरादा रखते हैं, हालांकि यह भी जोड़ना, या केवल यह सुनिश्चित करना कि केवल वैध बदलावों को पारित किया जा सके (बदलावों को एक दृश्य बनाकर), परिणाम प्रदान की गई सीमा से असंबंधित प्रतीत होते हैं - वही परिणाम इस बात पर ध्यान दिए बिना दिए जाते हैं कि सीमा शिफ्ट के अंदर या बाहर है या नहीं। – David

+0

दाएं, शीर्ष सबक्वायरी ने पूरी तरह से सीमा को अनदेखा किया - जोड़ा गया चेक –

0

एक तरह से जांच की जानी (अपने बदलाव के संकल्प के एक समारोह है) की आवश्यकता होती है हर बार मूल्य के लिए एक पंक्ति के साथ एक अस्थायी तालिका बनाने के लिए है।

यदि यह मिनट था तो दिन के लिए 60 * 24 = 1440 पंक्तियां होंगी; एक सप्ताह के लिए लगभग 10 के पंक्तियां।

फिर एसक्यूएल अपेक्षाकृत सरल है:

COUNT का चयन करें (1)
#minutes से M
वाम s.start_time और s.end_time
होने COUNT बीच m.checktime में शामिल होने की पाली रों (1) = 0

यह दिखाने में भी सक्षम है कि एक ही समय में कितनी बदलाव शामिल हैं।

निष्पादन समय आपके द्वारा वर्णित तराजू के बाद नगण्य होना चाहिए।

0

मैं तिथि सीमा देख रहा था और सोचा कि मैं इस प्रश्न पर फिर से जाऊंगा। मैं यहाँ मेरे चेहरे पर फ्लैट गिर सकता है, लेकिन ऐसा लगता है इन दोनों की स्थिति काफी

(1) Shift is not at beginning of range and has no left neighbour 

OR 

(2) Shift is not at end of range and has no right neighbour. 

की सराहना इस सबसे कारगर नहीं हो सकता है किया जाएगा।

CREATE TABLE times 
(
TimeID int, 
StartTime Time, 
EndTime Time 
) 

INSERT INTO times 
VALUES 
(1,'10:00:00','11:00:00'), 
(2,'11:00:00','12:00:00'), 
(3,'13:00:00','14:00:00'), 
(4,'14:30:00','15:00:00'), 
(5,'15:00:00','16:00:00'), 
(6,'16:00:00','17:00:00') 

declare @start_of_range time ='09:30:00' 
declare @end_of_range time = '17:30:00' 



select timeID,StartTime,EndTime 
from times s1 where 
-- No left neighbour and not at beginning of range 
    not exists 
    (select 1 from times s2 
    where s2.startTime < s1.startTime and s2.endTime >= s1.startTime 
    ) 
    and s1.StartTime>@start_of_range 
    or 
-- No right neighbour and not at end of range 
    not exists 
    (select 1 from times s2 
    where s2.startTime <= s1.endTime and s2.endTime > s1.endTime 
    ) 
    and s1.EndTime<@end_of_range 

परिणाम सेट

timeID StartTime EndTime 
1 10:00:00.0000000 11:00:00.0000000 
2 11:00:00.0000000 12:00:00.0000000 
3 13:00:00.0000000 14:00:00.0000000 
4 14:30:00.0000000 15:00:00.0000000 
6 16:00:00.0000000 17:00:00.0000000 

वास्तव में यह है, या तो सही पड़ोसियों या बाईं पड़ोसियों की जाँच करने के रूप में लंबे समय के रूप में आप यह सुनिश्चित करें कि शुरू करने और सीमा की समाप्ति चेक किया गया है केवल आवश्यक है, ताकि आप कर सकते थे एक डमी अंतराल के रूप में सीमा का आरंभ लागू करने और इस प्रकार सिर्फ सही पड़ोसियों की जाँच करें: -

select * from 
(
select timeID,StartTime,EndTime 
from times union select 0,@start_of_range,@start_of_range) s1 
where 
    not exists 
    (select 1 from times s2 
    where s2.startTime<=s1.endTime and s2.endTime > s1.endTime 
    ) 
    and s1.EndTime<@end_of_range 

परिणाम सेट

timeID StartTime EndTime 
0 09:30:00.0000000 09:30:00.0000000 
2 11:00:00.0000000 12:00:00.0000000 
3 13:00:00.0000000 14:00:00.0000000 
6 16:00:00.0000000 17:00:00.0000000 
संबंधित मुद्दे