2014-02-27 4 views
1

나는 다음과 같다 테이블이 있습니다 -SQL 서버 : 갭/섬, 날짜 시간, 연속 블록 365일 블록

tblMeterReadings

ID 미터를                 period_start               0 1,237,819,         는                     양
1       1               2014년 1월 1일 0시     2014년 1월 1일 00  을 period_end : 29:59      ,365,583,210 100.3
2       1               2014년 1월 1일 0시 반     2014년 1월 1일 0시 59분 59초           50.5
3       1               2014-01-01 01:00     2014-01-0 1 1시 29분 59초           70.7
4       1               2014년 1월 1일 1시 반     2014년 1월 1일 1시 59분 59초           900.1
5       1           400.0
6       1               2014년 1월 1일 2시 반     2014년 1월 1일 2시     2014년 1월 1일 2시 29분 59초               2014-01-01 02:59:59           200.3
7       1               2014년 1월 1일 3시     2014년 1월 1일 3시 29분 59초           100.8
8       1               2014-01-01 03:30     2014-01-01 03:59:59 012 37,819,        140.3

'2014-01-01 00:00' to '2014-01-01 3:59:59'에서 작은 "연속 블록"입니다.

실제 테이블에는 길이가 연속 된 "연속 블록"이 있습니다.

가장 최근의 것의 period_start 및 period_end를 찾아야합니다. 연속 365 일 완료 (미터 열로 파일 작성).

내가 말할 때 완전한 일 나는 00:00 to 23:59에 걸쳐있는 항목을 가진 날을 의미합니다. 내가 말할 때 계속 즉, 누락 된 날이 없어야 함을 의미합니다.

이 블록을 구성하는 모든 행을 연속 완료일 인으로 선택하고 싶습니다.

은 또한 같은 출력해야

          block_start을                                   block_end   0   1,237,819,                    total_amount_for_block
2013년 2월 26일 0시       2014년 2월 26일 23시 59분 59초                           1034234.5

이것은 나를 넘어서서 누군가가 해결할 수 있다면 ... 매우 인상 깊을 것입니다.

+1

어떻게 기간이 시작합니까 – podiluska

+0

islands''itzik 벤-의 GaN 격차를 검색해보세요? 하루 종일 시작하는이 시간은 항상 30 분입니까? 그럴 수 없다면 하루에 시작하여 다른 날에 끝낼 수 있습니까? –

+0

완전한 날짜 - 하루에 30 분마다 기록 하시겠습니까? – Rodion

답변

1

, 당신은 모든 날짜로 기간을 확대 할 필요가/: 1 개 초 간격으로 시작과 끝 사이의 시간. 이렇게하려면 숫자 표와 조인 할 필요가 있습니다 (숫자 표는 임의의 시스템보기에서 개체 ID의 순위를 매김으로써 즉석에서 생성됩니다.이 숫자는 하루의 초 수이기 때문에 TOP 86400으로 제한했습니다.

WITH Numbers AS 
( SELECT TOP (86400) 
      Number = ROW_NUMBER() OVER(ORDER BY a.object_id) - 1 
    FROM sys.all_objects a 
      CROSS JOIN sys.all_objects b 
    ORDER BY a.object_id 
), Grouped AS 
( SELECT r.meter, 
      Amount = CASE WHEN Number = 1 THEN r.Amount ELSE 0 END, 
      dt.[DateTime], 
      GroupingSet = DATEADD(SECOND, 
            -DENSE_RANK() OVER(PARTITION BY r.Meter 
                 ORDER BY dt.[DateTime]), 
            dt.[DateTime]) 
    FROM tblMeterReadings r 
      CROSS JOIN Numbers n 
      OUTER APPLY 
      ( SELECT [DateTime] = DATEADD(SECOND, n.Number, r.period_start) 
      ) dt 
    WHERE dt.[DateTime] <= r.Period_End 
) 
SELECT meter, 
     PeriodStart = MIN([DateTime]), 
     PeriodEnd = MAX([DateTime]), 
     Amount = SUM(Amount) 
FROM Grouped 
GROUP BY meter, GroupingSet 
HAVING DATEADD(YEAR, 1, MIN([DateTime])) < MAX([DateTime]); 

NB :

WITH Numbers AS 
( SELECT TOP (86400) 
      Number = ROW_NUMBER() OVER(ORDER BY a.object_id) - 1 
    FROM sys.all_objects a 
      CROSS JOIN sys.all_objects b 
    ORDER BY a.object_id 
) 
SELECT r.ID, r.meter, dt.[DateTime] 
FROM tblMeterReadings r 
     CROSS JOIN Numbers n 
     OUTER APPLY 
     ( SELECT [DateTime] = DATEADD(SECOND, n.Number, r.period_start) 
     ) dt 
WHERE dt.[DateTime] <= r.Period_End; 

당신은 다음하는 정상 격차와 섬의 그룹화를 수행하기 위해 연속적인 범위를 가지고 : 당신은 당신의 기간이 하루 이상)에 걸쳐 결코 언급 한 (가) Number 원인 금액에 가입 복제 할 수 있기 때문에, 즉 만 샘플 데이터에 대한 HAVING 절을 제거 각 ID

의 첫 번째 행에 대한 금액을 포함CASE WHEN Number = 1 THEN r.Amount ELSE 0 END를 사용하여 0으로 모든 중복을 설정하는 것이 필요하다 줄 것이다 :

meter | PeriodStart   | PeriodEnd   | Amount 
------+---------------------+---------------------+---------- 
1  | 2014-01-01 00:00:00 | 2014-01-01 03:59:59 | 1963 

+0

그 일을하는 것처럼 보입니다 ... 고마워요. 86400의 의미는 무엇입니까? 무슨 일이 일어나고 있는지 이해할 수 있도록 몇 가지 의견을 추가 할 수 있습니까? – benscammell

+0

또한 ... 만약 당신이 이것을 디자인했다면, 00:00:00 -> 00:30:00, 00:30:00 - 01:00:00, 또는 어떻게 지내십니까? – benscammell

+1

86400은 1 일의 초 수이며, 마침표가 1 일 이상 지속되지 않는 경우 필요한 최대 숫자입니다. 그렇습니다. 기간 만료일을 제외하고 '00:00:00 -> 00:30:00, 00:30:00 -> 01 : 00 : 00'으로 끝나는 것이 좋습니다. 이 특정 쿼리의 경우 큰 차이는 아니지만 다른 쿼리의 경우에는 다를 수 있습니다. 내 추론은이 기사에서 거의 다룰 내용이다 : [BETWEEN과 악마는 공통점이 무엇입니까?] (http://sqlblog.com/blogs/aaron_bertrand/archive/2011/10/19/what-do-between-and -the-devil-have-common.aspx) – GarethD

0

당신이 시도 할 수 : 당신의 단위 1 초이기 때문에

Select MIN(period_start) as "block start" 
    , MAX(period_end) as "block end" 
    , SUM(amount) as "total amount" 
FROM YourTable 
GROUP BY datepart(year, period_start) 
     , datepart(month, period_start) 
     , datepart(day, period_start) 
     , datepart(year, period_end) 
     , datepart(month, period_end) 
     , datepart(day, period_end) 
Having  datepart(year, period_start) = datepart(year, period_end) 
     AND datepart(month, period_start) = datepart(month, period_end) 
     AND datepart(day, period_start) = datepart(day, period_end) 
     AND datepart(hour, MIN(period_start)) = 0 
     AND datepart(minute,MIN(period_start)) = 0 
     AND datepart(hour, MAX(period_end)) = 23 
     AND datepart(minute,MIN(period_end)) = 59 
+0

이것이 문제를 해결하지 못한다고 생각합니다 ... 그보다 더 복잡합니다. – benscammell