2012-11-14 19 views
5

Czy istnieje prosty sposób to zrobić? Całkowicie między, mam na myśli, nie liczę datetimes 7 rano lub 7 pm, które są równe czasu rozpoczęcia lub zakończenia.Policz liczbę wystąpień 7AM lub 7PM między dwoma datetimes

Wyobrażam sobie, że można to zrobić za pomocą unixowego znacznika czasu w sekundach i odrobiny algebry, ale nie mogę tego rozgryźć.

Cieszę się, że mogę użyć czegoś w PLSQL lub zwykłym SQL.

Przykłady:

start    end    num_7am_7pm_between_dates 
2012-06-16 05:00 2012-06-16 08:00 1 
2012-06-16 16:00 2012-06-16 20:00 1 
2012-06-16 05:00 2012-06-16 07:00 0 
2012-06-16 07:00 2012-06-16 19:00 0 
2012-06-16 08:00 2012-06-16 15:00 0 
2012-06-16 05:00 2012-06-16 19:01 2 
2012-06-16 05:00 2012-06-18 20:00 6 

Odpowiedz

3

myślę, że to może być zmniejszona, ale dalej nie mam Oracle do mojej dyspozycji, aby w pełni przetestować ten Oracle SQL:

SELECT StartDate 
    , EndDate 
    , CASE WHEN TRUNC(EndDate) - TRUNC(StartDate) < 1 
      AND TO_CHAR(EndDate, 'HH24') > 19 
      AND TO_CHAR(StartDate, 'HH24') < 7 
      THEN 2 
      WHEN TRUNC(EndDate) - TRUNC(StartDate) < 1 
      AND (TO_CHAR(EndDate, 'HH24') > 19 
       OR TO_CHAR(StartDate, 'HH24') < 7) 
      THEN 1 
      WHEN TRUNC(EndDate) - TRUNC(StartDate) > 0 
      AND TO_CHAR(EndDate, 'HH24') > 19 
      AND TO_CHAR(StartDate, 'HH24') < 7 
      THEN 2 + ((TRUNC(EndDate) - TRUNC(StartDate)) * 2) 
      WHEN TRUNC(EndDate) - TRUNC(StartDate) > 0 
      AND TO_CHAR(EndDate, 'HH24') > 19 
       OR TO_CHAR(StartDate, 'HH24') < 7 
      THEN 1 + ((TRUNC(EndDate) - TRUNC(StartDate)) * 2) 
      ELSE 0 
     END 
FROM MyTable; 

Dzięki @ABCade dla Fiddle, wygląda moja Case Logic można kondensować dalej:

SELECT SDate 
    , EDate 
    , CASE WHEN TO_CHAR(EDate, 'HH24') > 19 
      AND TO_CHAR(SDate, 'HH24') < 7 
      THEN 2 + ((TRUNC(EDate) - TRUNC(SDate)) * 2) 
      WHEN TO_CHAR(EDate, 'HH24') > 19 
       OR TO_CHAR(SDate, 'HH24') < 7 
      THEN 1 + ((TRUNC(EDate) - TRUNC(SDate)) * 2) 
      ELSE 0 
     END AS MyCalc2 
    FROM MyTable; 
+1

Wygląda na to, że jesteś we właściwy sposób - możesz użyć tego skrzypca http://www.sqlfiddle.com/#!4/baead/2 –

+0

Dzięki za skrzypce. Patrząc na to teraz. –

+0

nie jest jeszcze ok. [http://www.sqlfiddle.com/#!4/baead/6](http://www.sqlfiddle.com/#!4/baead/6). Po pierwsze, policzymy 1, gdy obie godziny będą mniejsze niż 7 (powinny wynosić zero) lub oba są większe niż 19. Po drugie, nie uwzględnia minut. Na przykład nie liczy 19:01. –

2

To może nie mają najlepszą wydajność, ale może pracować dla Ciebie:

select sdate, edate, count(*) 
    from (select distinct edate, sdate, sdate + (level/24) hr 
      from t 
     connect by sdate + (level/24) <= edate) 
where to_char(hr, 'hh') = '07' 
group by sdate, edate 

UPDATE: Co do @ komentarz FlorinGhita za - stałe zapytania zawierać zera wystąpienia

select sdate, edate, sum(decode(to_char(hr, 'hh'), '07',1,0)) 
    from (select distinct edate, sdate, sdate + (level/24) hr 
      from t 
     connect by sdate + (level/24) <= edate) 
group by sdate, edate 
+0

wyklucza wiersze zero wystąpień –

+0

@FlorinGhita, dzięki stałej moje zapytanie –

+0

+1 Myślę, że jest ok teraz. –

1

Czy tak (SQL)

declare @table table (start datetime, ends datetime) 
insert into @table select'2012-06-16 05:00','2012-06-16 08:00' --1 
insert into @table select'2012-06-16 16:00','2012-06-16 20:00' --1 
insert into @table select'2012-06-16 05:00','2012-06-16 07:00' --0 
insert into @table select'2012-06-16 07:00','2012-06-16 19:00' --0 
insert into @table select'2012-06-16 08:00','2012-06-16 15:00' --0 
insert into @table select'2012-06-16 05:00','2012-06-16 19:01' --2 
insert into @table select'2012-06-16 05:00','2012-06-18 20:00' --6 
insert into @table select'2012-06-16 07:00','2012-06-18 07:00' --3 


Declare @From DATETIME 
Declare @To DATETIME 

select @From = MIN(start) from @table 
select @To = max(ends) from @table 

;with CTE AS 
( 
     SELECT distinct 
    DATEADD(DD,DATEDIFF(D,0,start),0)+'07:00' AS AimTime 
    FROM @table 
),CTE1 AS 
( 
    Select AimTime 
    FROM CTE 

    UNION ALL 

    Select DATEADD(hour, 12, AimTime) 
From CTE1 
WHERE AimTime< @To 
) 


select start,ends, count(AimTime) 
from CTE1 right join @table t 
on t.start < CTE1.AimTime and t.ends > CTE1.AimTime 
group by start,ends 
3

miałem zabawy pisząc następujące rozwiązanie:

with date_range as (
    select min(sdate) as sdate, max(edate) as edate 
    from t 
), 
all_dates as (
    select sdate + (level-1)/24 as hour 
    from date_range 
    connect by level <= (edate-sdate) * 24 + 1 
), 
counts as (
    select t.id, count(*) as c 
    from all_dates, t 
    where to_char(hour, 'HH') = '07' 
    and hour > t.sdate and hour < t.edate 
    group by t.id 
) 
select t.sdate, t.edate, nvl(counts.c, 0) 
from t, counts 
where t.id = counts.id(+) 
order by t.id; 

Dodałem kolumnę id do tabeli, jeśli zakres dat nie jest unikalny.

http://www.sqlfiddle.com/#!4/5fa19/13

Powiązane problemy