2011-12-16 2 views
5

다음과 같은 열이있는 테이블이 있습니다.SQL Server 쿼리 도움말, 초보자가 필요

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 

내가 지금 할 싶은 각 이벤트에 대해 시간에 나에게 총 가동 중지 시간을 제공 쿼리,하지만 무엇을 구축 할 수 있었다 한 달에 한 번씩 매일 총 작동 중지 시간을 제공하는 쿼리를 작성하십시오. 이드는 왼쪽으로 컴 피디드를 갖고 싶다. 그리고 같은 달의 칼럼에있는 컴피드의 오른쪽에있는 달의 각 날을 보낸다. 이드는 가동 중지 시간이없는 날을 null로 간주합니다. 이 테이블이 설정된 방식으로 가능합니까?

+0

질문 : 하루 동안 자정 이후의 해결 인 경우에도 문제가되는 시작일이라고 생각하십니까? – fge

+3

확실히 가능합니다. 너 뭐 해봤 니? DATEDIFF에 대한. 서를 시작해야합니다. 또한 언젠가는 다음 날까지 교차하는 중단 시간으로 무엇을 할 지 결정해야합니다. 이것은 일을 더욱 복잡하게 만듭니다. 그러나 확실히 가능합니다. – paulbailey

답변

1

1 단계 : 원하는 "시간 블록"이 포함 된 임시 테이블을 설정하십시오. 이러한 블록은 시간 범위에 관계없이 사용할 수 있습니다. 당신의 예에서, 그것은 그 달에 영원한 날 (24 시간의 기간) 동안의 1 개의 입구 일 것이다.

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 

(I :

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

데이터에이 테이블을 왼쪽 외부 결합하면 그 날을 발생하는 어떠한 경보가 없었다하더라도, 시간 블록 (일) 당 적어도 하나 개의 행을 얻을 보장 이 코드를 테스트 할 수는 없지만 일부 디버깅이 필요할 수 있습니다. 그러나 개념은 견고합니다. 부분 시간 반올림에 대해 걱정할 필요가 있으며> 및 < 또는> = 및 < = 일이 까다로울 수 있습니다. 알람이 시작되거나 블록 경계와 정확히 동일한 시점에서 끝나는 경우)

(210)

편집/부칙

여기 (이 코드는, 내가 테스트) 루틴에서 사용되는 임시 테이블을 설정하는 매우 기본적인 방법 :

-- 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의을함으로써 다음날 시작, 당신은 당신의 큰 덩어리와 lessthans 조심해야 해. 세부 사항은 매우 까다 롭고 까다로워 질 수 있으며, 많은 경우 에지 테스트를 수행해야합니다 (알람이 시작되거나 종료되는 경우 정확히 Dec 16 2011 00 : 00.000). 나는 그걸로 갈 것인데 왜냐하면 전반적으로 'Dec 16, 2011 23 : 59.997'과 같은 쓰레기보다 코드 작성이 더 간단하기 때문이다.

+2

FYI, 나는 이런 문제를 종이와 연필로 해결합니다. 가능한 모든 중첩 및 중첩되지 않는 시간 범위에 대해 선분을 그려 논리를 적절하게 계산합니다. –

+0

필자는 이전에 임시 테이블을 사용한 적이 없었습니다! 어떤 범위로 사용할 지 어떻게 말합니까? 이것에 대한 도움을 주셔서 감사합니다. 내가 기본적인 SQL 물건을 할 수 있지만이 고급 물건 내 머리를 통해 방법입니다! –

+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 개월 이내에 존재하는 모든 요일의 목록을 가지고 있어야합니다. 이 목록을 가져 오면 위 쿼리의 결과가 LEFT OUTER JOIN이됩니다 (먼저 ORDER BY를 제거해야합니다).

1

날짜와 왼쪽 결합으로 임시 테이블을 채우는 주된 교장이 사실 임에도 불구하고 필립 켈리의 대답의 case 문은 작동하지 않습니다. 내 버전에서는 입력 할 날짜와보고 일 수와 같은 변수를 사용했습니다.

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 중고 초 가지며 초는 DATEDIFF위한 시간을 사용할 때 0으로 합산 할 분의 차이가 60 행으로 각각 합산 한 후 3600.0 나눈.