JaMaBing
JaMaBing

Reputation: 1041

Select data grouped by time over midnight

I have a table like:

ID       TIMEVALUE
-----    -------------
1        06.07.15 06:43:01,000000000
2        06.07.15 12:17:01,000000000
3        06.07.15 18:21:01,000000000
4        06.07.15 23:56:01,000000000
5        07.07.15 04:11:01,000000000
6        07.07.15 10:47:01,000000000
7        07.07.15 12:32:01,000000000
8        07.07.15 14:47:01,000000000

and I want to group this data by special times.
My current query looks like this:

SELECT TO_CHAR(TIMEVALUE, 'YYYY\MM\DD'), COUNT(ID), 
  SUM(CASE WHEN TO_CHAR(TIMEVALUE, 'HH24MI') <=700 THEN 1 ELSE 0 END) as morning,
  SUM(CASE WHEN TO_CHAR(TIMEVALUE, 'HH24MI') >700 AND TO_CHAR(TIMEVALUE, 'HH24MI') <1400 THEN 1 ELSE 0 END) as daytime,
  SUM(CASE WHEN TO_CHAR(TIMEVALUE, 'HH24MI') >=1400 THEN 1 ELSE 0 END) as evening FROM Table
WHERE TIMEVALUE >= to_timestamp('05.07.2015','DD.MM.YYYY')
GROUP BY TO_CHAR(TIMEVALUE, 'YYYY\MM\DD')

and I am getting this output

day          overall     morning    daytime    evening 
-----        ---------
2015\07\05   454         0          0          454
2015\07\06   599         113        250        236
2015\07\07   404         139        265        0

so that is fine grouping on the same day (0-7 o'clock, 7-14 o'clock and 14-24 o'clock)
But my question now is: How can I group over midnight?

For example count from 6-14 , 14-23 and 23-6 o'clock on next day.

I hope you understand my question. You are welcome to even improve my upper query if there is a better solution.

Upvotes: 4

Views: 486

Answers (3)

sstan
sstan

Reputation: 36483

EDIT: It is tested now: SQL Fiddle

The key is simply to adjust the group by so that anything before 6am gets grouped with the previous day. After that, the counts are pretty straight-forward.

SELECT TO_CHAR(CASE WHEN EXTRACT(HOUR FROM timevalue) < 6
                    THEN timevalue - 1
                    ELSE timevalue
                    END, 'YYYY\MM\DD') AS day, 
       COUNT(*) AS overall, 
       SUM(CASE WHEN EXTRACT(HOUR FROM timevalue) >= 6 AND EXTRACT(HOUR FROM timevalue) < 14
                THEN 1 ELSE 0 END) AS morning,
       SUM(CASE WHEN EXTRACT(HOUR FROM timevalue) >= 14 AND EXTRACT(HOUR FROM timevalue) < 23
                THEN 1 ELSE 0 END) AS daytime,
       SUM(CASE WHEN EXTRACT(HOUR FROM timevalue) < 6 OR EXTRACT(HOUR FROM timevalue) >= 23
                THEN 1 ELSE 0 END) AS evening
FROM my_table
WHERE timevalue >= TO_TIMESTAMP('05.07.2015','DD.MM.YYYY')
GROUP BY TO_CHAR(CASE WHEN EXTRACT(HOUR FROM timevalue) < 6
                    THEN timevalue - 1
                    ELSE timevalue
                    END, 'YYYY\MM\DD');

Upvotes: 1

Wernfried Domscheit
Wernfried Domscheit

Reputation: 59455

Maybe you can do it like this (with some reformatting or PIVOT):

WITH spans AS 
    (SELECT TIMESTAMP '2015-01-01 00:00:00' + LEVEL * INTERVAL '1' HOUR AS start_time
    FROM dual
    CONNECT BY TIMESTAMP '2015-01-01 00:00:00' + LEVEL * INTERVAL '1' HOUR < LOCALTIMESTAMP),
t AS 
    (SELECT start_time, lead(start_time, 1) OVER (ORDER BY start_time) AS end_time, ROWNUM AS N
    FROM spans
    WHERE EXTRACT(HOUR FROM start_time) IN (6,14,23))
SELECT N, start_time, end_time, COUNT(*) AS ID_COUNT,
    DECODE(EXTRACT(HOUR FROM start_time), 6,'morning', 14,'daytime', 23,'evening') AS daytime
FROM t
    JOIN YOUR_TABLE WHERE TIMEVALUE BETWEEN start_time AND end_time
GROUP BY N;

Of course, the initial time value ('2015-01-01 00:00:00' in my example) has to be lower than the least date in your table.

Upvotes: 0

Ponder Stibbons
Ponder Stibbons

Reputation: 14848

Substract 1 day from timevalue for times lower than '06:00' at first and then:

SQLFiddle demo

select TO_CHAR(day, 'YYYY\MM\DD') day, COUNT(ID) cnt, 
    SUM(case when '23' < tvh or  tvh <= '06' THEN 1 ELSE 0 END) as midnight,
    SUM(case when '06' < tvh and tvh <= '14' THEN 1 ELSE 0 END) as daytime,
    SUM(case when '14' < tvh and tvh <= '23' THEN 1 ELSE 0 END) as evening
  FROM (
    select id, to_char(TIMEVALUE, 'HH24') tvh,
        trunc(case when (to_char(timevalue, 'hh24') <= '06') 
                   then timevalue - interval '1' day  
                   else timevalue end) day
      from t1
    )
  GROUP BY day

Upvotes: 1

Related Questions