2011-12-16 11 views
5

मेरे पास एक तालिका है जिसमें निम्न कॉलम हैं।SQL सर्वर क्वेरी सहायता की आवश्यकता है, न्यूबी

id, 
compid(used to identify a piece of equipment), 
startalarmdate, 
endalarmdate 

जब उपकरण का एक टुकड़ा अलार्म में चला जाता है, मैं compid और startalarmdate (वर्तमान समय के साथ) तालिका में डालने और जब उपकरण अलार्म से बाहर आता है मैं में है कि एक ही पंक्ति में अशक्त में भरने वर्तमान समय के साथ endalarmdate कॉलम।

तो मैं तालिका में कुछ इस तरह से अंत

417,6,Sun Oct 30 18:48:17 CDT 2011,Mon Oct 31 09:49:21 CDT 2011 
422,6,Mon Oct 31 10:19:19 CDT 2011,Mon Oct 31 12:19:22 CDT 2011 
427,6,Mon Oct 31 20:19:56 CDT 2011,Tue Nov 01 09:50:59 CDT 2011 
429,6,Tue Nov 01 21:51:41 CDT 2011,Wed Nov 02 09:52:37 CDT 2011 
432,6,Wed Nov 02 21:23:23 CDT 2011,Fri Nov 04 16:26:29 CDT 2011 

मैं एक प्रश्न है कि मुझे प्रत्येक घटना के लिए घंटों में कुल डाउनटाइम देता है, लेकिन क्या मैं अब है करना चाहते हैं का निर्माण करने में सक्षम था एक प्रश्न बनाएं जो मुझे महीने के प्रत्येक दिन के लिए डाउनटाइम में कुल घंटे देता है। आईडी को बाईं ओर सभी तरह से कंप्यूड रखना है, फिर उसी पंक्ति पर कॉलम में कंप्यूड के दाईं ओर महीने के प्रत्येक दिन होता है। आईडी को बिना डाउनटाइम वाले दिनों की तरह शून्य होना चाहिए। क्या यह तालिका सेट अप करने के तरीके से ऐसा करना संभव है?

+0

प्रश्न: एक दिन के लिए, मुझे लगता है कि यह प्रारंभ तिथि है जो कि मध्यरात्रि के बाद भी है, ठीक है? – fge

+3

यह निश्चित रूप से संभव है। आपने क्या प्रयास किया है आपको प्रारंभ करने के लिए DATEDIFF के लिए प्रलेखन को देखना चाहिए। साथ ही, आपको यह तय करने की आवश्यकता होगी कि डाउनटाइम के साथ क्या करना है जो एक दिन से अगले दिन तक पार हो जाता है। इससे चीजों को और जटिल बना दिया जाता है, लेकिन यह निश्चित रूप से करने योग्य है। – paulbailey

उत्तर

1

चरण 1: वांछित "टाइम ब्लॉक" युक्त एक अस्थायी तालिका सेट करें जिसे आप कुल करना चाहते हैं। ये ब्लॉक किसी भी समय के लिए हो सकते हैं; आपके उदाहरण में, यह महीने में (24 घंटे की अवधि) के लिए एक प्रविष्टि होगी।

SELECT 
    tr.RangeStart -- Use start of each time block to identify the block 
    ,md.CompId  -- With left outer join, will be 0 or more rows for each block 
    ,sum(datediff(hh 
       ,case 
        when tr.RangeStart > md.StartAlarmDate then tr.RangeStart 
        else md.StartAlarmDate 
       end 
       ,case 
        when tr.RangeEnd > md.EndAlarmDate then tr.RangeEnd 
        else md.EndAlarmDate 
       end)) HoursInRange 
from #TimeRanges tr 
    left outer join MyData md 
    on md.StartAlarmDate < tr.RangeEnd 
    and md.EndAlarmDate > tr.From 
group by 
    tr.RangeStart 
,md.CompId 

(मैं:

CREATE TABLE #TimeRanges 
(
    RangeStart datetime not null 
    ,RangeEnd datetime not null 
) 

अपने डेटा पर इस तालिका वाम बाहरी शामिल होने आप समय ब्लॉक (दिन) के अनुसार कम से कम एक पंक्ति मिलता है, भले ही कोई उस दिन उत्पन्न अलार्म थे सुनिश्चित करता है इस कोड का परीक्षण नहीं कर सकता है, कुछ डिबगिंग की आवश्यकता हो सकती है - लेकिन अवधारणा ठोस है। मैं आपको आंशिक घंटों के दौर के बारे में चिंता करने दूंगा, और चाहे आप चाहें> और <, या> = और < = (चीजें मुश्किल हो सकती हैं यदि कोई अलार्म प्रारंभ सीमा के समान समय पर एक ही बिंदु पर शुरू होता है और/या समाप्त होता है)


संपादित करें/अनुशेष

यहाँ एक काफी बुनियादी अस्थायी दिनचर्या में प्रयोग किया जाता तालिका स्थापित करने के लिए जिस तरह से (इस कोड, मैं परीक्षण किया) है: कि

-- Set up and initialize some variables 
DECLARE 
    @FirstDay  datetime 
,@NumberOfDays int 

SET @FirstDay = 'Oct 1, 2011' -- Without time, this makes it "midnight the morning of" that day 
SET @NumberOfDays = 91 -- Through Dec 31 


-- Creates a temporary table that will persist until it is dropped or the connection is closed 
CREATE TABLE #TimeRanges 
    (
    RangeStart datetime not null 
    ,RangeEnd datetime not null 
) 

-- The order in which you add rows to the table is irrelevant. By adding from last to first, I 
-- only have to fuss around with one variable, instead of two (the counter and the end-point) 

WHILE @NumberOfDays >= 0 
BEGIN 
    INSERT #TimeRanges (RangeStart, RangeEnd) 
    values (dateadd(dd, @NumberOfDays, @FirstDay)  -- Start of day 
      ,dateadd(dd, @NumberOfDays + 1, @FirstDay)) -- Start of the next day 


    SET @NumberOfDays = @NumberOfDays - 1 
END 


-- Review results 
SELECT * 
from #TimeRanges 
order by RangeStart 


-- Not necessary, but doesn't hurt, especially when testing code 
DROP TABLE #TimeRanges 

नोट RangeEnd बनाकर अगले दिन की शुरुआत, आपको अपने बढ़ने और समलैंगिकों से सावधान रहना होगा। विवरण बहुत ही जटिल और उग्र हो सकते हैं, और आप बहुत सारे एज-केस परीक्षण करना चाहते हैं (अगर अलार्म शुरू होता है, या समाप्त होता है, ठीक उसी तरह 16 दिसंबर 2011 00: 00.000)। मैं इसके साथ जाऊंगा, क्योंकि कुल मिलाकर 16 दिसंबर, 2011 23: 59.997 '

+2

एफवाईआई, जिस तरह से मैं इस तरह की समस्याओं को हल करता हूं वह पेपर और पेंसिल के साथ होता है। सभी संभावित ओवरलैपिंग और गैर-ओवरलैपिंग टाइम-स्पैन के लिए लाइन सेगमेंट बनाएं, और तदनुसार तर्क दें। –

+0

Ive पहले कभी एक temp तालिका का उपयोग नहीं किया! मैं इसे कैसे बता सकता हूं कि किस सीमा का उपयोग करना है? इस पर मदद के लिए धन्यवाद। मैं बुनियादी एसक्यूएल सामान कर सकता हूं लेकिन यह उन्नत सामान मेरे सिर पर रास्ता है! –

+0

अस्थायी तालिका पर अनुभाग जोड़ा गया –

1

@paulbailey द्वारा उल्लिखित अनुसार, आप डाउनटाइम की मात्रा प्राप्त करने के लिए DATEDIFF फ़ंक्शन का उपयोग करना चाहते हैं ।

.. तिथियाँ और डाउनटाइम अवधि (मैं थोड़ा अधिक स्तंभ है कि आप की आवश्यकता हो सकती जोड़ रहा) निकालने के लिए

SELECT compid, 
     YEAR(startalarmdate) AS [Year], 
     MONTH(startalarmdate) AS [Month], 
     DAY(startalarmdate) AS [Day], 
     DATEDIFF(ss, startalarmdate, endalarmdate) AS DowntimeInSeconds --You will need to convert thid later to the format you wish to use 
FROM YourTable 
/* WHERE CLAUSE - Most probably a date range */ 

अब इस आप प्रत्येक दिन है कि एक अन्तराल था के लिए सेकंड में डाउनटाइम देता है।

दिन प्रति दिन डाउनटाइम की मात्रा प्राप्त करने के लिए दिन के अनुसार समूह करना और डाउनटाइम को कम करना (फिर से अधिक कॉलम जो आपको चाहिए) जोड़ना आसान है।

SELECT compid, 
     [Year], 
     [Month], 
     [Day], 
     SUM(DowntimeInSeconds) AS TotalDowntimeInSeconds 
FROM (SELECT compid, 
      YEAR(startalarmdate) AS [Year], 
      MONTH(startalarmdate) AS [Month], 
      DAY(startalarmdate) AS [Day], 
      DATEDIFF(ss, startalarmdate, endalarmdate) AS DowntimeInSeconds --You will need to convert thid later to the format you wish to use 
     FROM YourTable 
     /* WHERE CLAUSE - Most probably a date range */) AS GetDowntimes 
GROUP BY compid, [Year], [Month], [Day] 
ORDER BY [Year], [Month], [Day], compid 

और मेरा मानना ​​है कि इससे आपको यह कहने में मदद करनी चाहिए कि आप कहां जाना चाहते हैं।

संपादित करें: ऐसे दिनों के लिए जिनके पास इस परिणाम में कोई डाउनटाइम शामिल नहीं है, आपको पहले एक महीने में मौजूद सभी दिनों की एक सूची की आवश्यकता है। आप इस सूची को लेते हैं और आप बाएं बाहरी उपरोक्त क्वेरी से परिणाम में शामिल होते हैं (आपको पहले ऑर्डर को हटाना होगा)।

1

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

DECLARE @StartDate DATETIME, @Days INT 
SELECT @StartDate = GETDATE(), 
     @Days = 5 

-- REMOVE ANY TIME FROM THE STARTDATE 
SET @StartDate = DATEADD(DAY, 0, DATEDIFF(DAY, 0, @StartDate)) 

-- CREATE THE TEMP TABLE OF DATES USING THE SAME METHODOLOGY 
DECLARE @Dates TABLE (AlarmDate SMALLDATETIME NOT NULL PRIMARY KEY) 
WHILE (@Days > 0) 
BEGIN 
    INSERT @Dates VALUES (DATEADD(DAY, @Days, @StartDate)) 
    SET @Days = @Days - 1 
END 

-- NOW SELECT THE DATA 
SELECT AlarmDate, 
     CompID, 
     CONVERT(DECIMAL(10, 2), ISNULL(SUM(DownTime), 0)/3600.0) [DownTime] 
FROM @Dates 
     LEFT JOIN 
     ( SELECT DATEADD(DAY, 0, DATEDIFF(DAY, 0, StartAlarmDate)) [StartAlarmDate], 
        CompID, 
        DATEDIFF(SECOND, StartAlarmDate, CASE WHEN EndAlarmDate >= DATEADD(DAY, 1, DATEDIFF(DAY, 0, StartAlarmDate)) THEN DATEADD(DAY, 1, DATEDIFF(DAY, 0, StartAlarmDate)) ELSE EndAlarmDate END) [DownTime] 
      FROM [yourTable] 
      UNION ALL 
      SELECT DATEADD(DAY, 0, DATEDIFF(DAY, 0, EndAlarmDate)) [Date], 
        CompID, 
        DATEDIFF(SECOND, DATEADD(DAY, 1, DATEDIFF(DAY, 0, StartAlarmDate)), EndAlarmDate) [DownTime] 
      FROM [yourTable] 
      WHERE EndAlarmDate >= DATEADD(DAY, 1, DATEDIFF(DAY, 0, StartAlarmDate)) 
     ) data 
      ON StartAlarmDate = AlarmDate 
GROUP BY AlarmDate, CompID 

मैं तारीख diff के लिए इस्तेमाल किया सेकंड है और 3600.0 से विभाजित करने के बाद सेकंड के लिए एक मिनट का एक अंतर के साथ 60 पंक्तियों के रूप में अभिव्यक्त किया गया है प्रत्येक 0 का योग जब एक DateDiff के लिए घंटों का उपयोग कर जाएगा।

संबंधित मुद्दे