Skip to Main Content
  • Questions
  • Query to get the count of records every two hours

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, Usha .

Asked: June 07, 2017 - 10:13 am UTC

Last updated: June 13, 2017 - 9:54 am UTC

Version: 12.1.0.2.0

Viewed 10K+ times! This question is

You Asked

I have a record creation time stamp in my table . I need to write a query to get the count of records every two hours between two specific dates.
Starting from 1st January 2017 till today.

I have the following table USER

USER_ID CREATION_DATE
1 01-JAN-2017 00:00:00
2 01-JAN-2017 01:00:00
3 01-JAN-2017 01.22:00
4 02-JAN-2017 01:00:00

I need a query which would give me result like below

DATE | 00-02 hr| 02-04 hr| 04-06 hr|06-08 hr|08-10 hr|10-12 hr|12-14 hr| ....
01-JAN-2017| 3 | 0 | 0 |0 |0 |0 |0 |..
02-JAN-2017|1 | 0 | 0 |0 |0 |0 |0 |..
.
.
.

and Chris said...

You need a two-step process:

- Map the times into 2 hour buckets
- Pivot the results of this

You can do the first step by:

- Getting the hour of day
- Dividing this by two
- Returning the floor of this

So 01:00 -> 0, 02:00 -> 1, 03:00 -> 1, 04:00 -> 2, etc.

You can do this with something like this:

floor(extract(hour from to_timestamp(dt))/2)


Then find how many rows are in each bucket for each day by getting the count(*) partitioned by day and the calculation above:

count(*) over (
  partition by trunc(dt), floor(extract(hour from to_timestamp(dt))/2)
)


Finally you need to pivot the results to get the columns as rows. Return the min (or max) count for each bucket you've determined above.

Put this all together and you get:

with rws as (
  select trunc(sysdate)+level/20 dt 
  from   dual connect by level <= 20
), cts as (
  select trunc(dt) dy, 
         count(*) over (
           partition by trunc(dt), floor(extract(hour from to_timestamp(dt))/2)
         ) ct,
         floor(extract(hour from to_timestamp(dt))/2) hr
  from rws
)
  select *
  from   cts
  pivot (
    min(ct) for hr in (0 as "0-2", 1 as "2-4", 3 as "4-6", 4 as "6-8") --etc.
  );

DY                    0-2  2-4  4-6  6-8  
09-JUN-2017 00:00:00  1    2    2    2    
10-JUN-2017 00:00:00  1    


For more on pivoting, read: https://blogs.oracle.com/sql/how-to-convert-rows-to-columns-and-back-again-with-sql-aka-pivot-and-unpivot

Rating

  (4 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

Aggregate and Pivot

Rajeshwaran, Jeyabal, June 09, 2017 - 10:19 am UTC

demo@ORA11G> create table t as
  2  select object_id, trunc(sysdate,'Y') + rownum/24 dt
  3  from all_objects
  4  where rownum <=50 ;

Table created.

demo@ORA11G> select t.*, to_char(dt,'hh24') hh24,floor(to_char(dt,'hh24')/2) grp
  2  from t
  3  where dt between to_date('02-jan-2017','dd-mon-yyyy')
  4    and to_date('04-jan-2017','dd-mon-yyyy')
  5  /

 OBJECT_ID DT          HH        GRP
---------- ----------- -- ----------
      1238 02-JAN-2017 00          0
      1239 02-JAN-2017 01          0
      1240 02-JAN-2017 02          1
      1241 02-JAN-2017 03          1
      1242 02-JAN-2017 04          2
      1243 02-JAN-2017 05          2
      1244 02-JAN-2017 06          3
      1245 02-JAN-2017 07          3
      1246 02-JAN-2017 08          4
      1247 02-JAN-2017 09          4
      1248 02-JAN-2017 10          5
      1249 02-JAN-2017 11          5
      1250 02-JAN-2017 12          6
      1251 02-JAN-2017 13          6
      1252 02-JAN-2017 14          7
      1253 02-JAN-2017 15          7
      1254 02-JAN-2017 16          8
      1255 02-JAN-2017 17          8
      1256 02-JAN-2017 18          9
      1257 02-JAN-2017 19          9
      1258 02-JAN-2017 20         10
      1259 02-JAN-2017 21         10
      1260 02-JAN-2017 22         11
      1261 02-JAN-2017 23         11
      1262 03-JAN-2017 00          0
      1263 03-JAN-2017 01          0
      1264 03-JAN-2017 02          1

27 rows selected.

demo@ORA11G> select trunc(dt) dt, floor( to_char(dt,'hh24')/2) grp,count(*) cnt
  2  from t
  3  where dt between to_date('02-jan-2017','dd-mon-yyyy')
  4    and to_date('04-jan-2017','dd-mon-yyyy')
  5  group by trunc(dt), floor( to_char(dt,'hh24')/2)
  6  /

DT                 GRP        CNT
----------- ---------- ----------
02-JAN-2017          4          2
02-JAN-2017          6          2
02-JAN-2017          2          2
02-JAN-2017         10          2
02-JAN-2017          5          2
02-JAN-2017         11          2
02-JAN-2017          1          2
02-JAN-2017          7          2
02-JAN-2017          8          2
03-JAN-2017          0          2
02-JAN-2017          0          2
03-JAN-2017          1          1
02-JAN-2017          3          2
02-JAN-2017          9          2

14 rows selected.

demo@ORA11G> set linesize 250
demo@ORA11G> select *
  2  from (
  3  select trunc(dt) dt, floor( to_char(dt,'hh24')/2) grp,count(*) cnt
  4  from t
  5  where dt between to_date('02-jan-2017','dd-mon-yyyy')
  6    and to_date('04-jan-2017','dd-mon-yyyy')
  7  group by trunc(dt), floor( to_char(dt,'hh24')/2)
  8       )
  9  pivot(
 10    max(cnt)
 11    for grp in ( 1 as "00-02",2 as "03-04",3 as "05-06",
 12                  4 as "07-08",5 as "09-10",6 as "11-12",
 13                  7 as "13-14",8 as "15-16",9 as "17-18",
 14                  10 as "19-20",11 as "21-22",12 as "23-24") )
 15  /

DT               00-02      03-04      05-06      07-08      09-10      11-12      13-14      15-16   17-18         19-20      21-22      23-24
----------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
03-JAN-2017          1
02-JAN-2017          2          2          2          2          2          2          2          2       2             2          2

demo@ORA11G>

Aggregate and Pivot

Rajeshwaran, Jeyabal, June 09, 2017 - 10:39 am UTC

sorry, the above Pivot query is incorrect, fixed it below.

demo@ORA11G> select *
  2  from (
  3  select trunc(dt) dt, floor(to_number(to_char(dt,'hh24'))/2) grp,count(*) cnt
  4  from t
  5  where dt between to_date('02-jan-2017','dd-mon-yyyy')
  6    and to_date('04-jan-2017','dd-mon-yyyy')
  7  group by trunc(dt), floor(to_number(to_char(dt,'hh24'))/2)
  8       )
  9  pivot(
 10    sum(cnt)
 11    for grp in ( 0 as "00-02",1 as "03-04",2 as "05-06",
 12                  3 as "07-08",4 as "09-10",5 as "11-12",
 13                  6 as "13-14",7 as "15-16",8 as "17-18",
 14                  9 as "19-20",10 as "21-22",11 as "23-24") )
 15  order by dt
 16  /

DT               00-02      03-04      05-06      07-08      09-10      11-12      13-14      15-16   17-18         19-20      21-22      23-24
----------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
02-JAN-2017          2          2          2          2          2          2          2          2       2             2          2          2
03-JAN-2017          2          1

demo@ORA11G>

Very much helpfull

Usha S, June 12, 2017 - 5:51 pm UTC

The whole concept of PIVOT is very useful in such scenarios where some kind of reporting is required. The answer was very helpful to deal with such kind of requirements.
Chris Saxon
June 13, 2017 - 9:54 am UTC

Thanks Usha.

time Interval

Dileep, September 23, 2021 - 9:27 am UTC

Thanks for your valuable Information.

More to Explore

Analytics

Analytic SQL got you confused? Check out Connor McDonald's complete video course.