Counting records and grouping them by the hour - sql

I'm trying to count the records in my table and grouping them by hour, i'm getting results with my query but I want it to return every hour even if there are no records.
My current query is,
SELECT nvl(count(*),0) AS transactioncount, trunc(date_modified, 'HH') as TRANSACTIONDATE
FROM TABLE
WHERE date_modified between to_date('23-JAN-19 07:00:00','dd-MON-yy hh24:mi:ss') and to_date('24-Jan-19 06:59:59','dd-MON-yy hh24:mi:ss')
group by trunc(date_modified, 'HH');
This returns a result like this,
TRANSACTIONCOUNT | TRANSACTIONDATE
43 | 23-Jan-19 07:00:00
47 | 23-Jan-19 08:00:00
156 | 23-Jan-19 14:00:00
558 | 23-Jan-19 15:00:00
What I want is for it to return every hour between my 2 dates so,
TRANSACTIONCOUNT | TRANSACTIONDATE
43 | 23-Jan-19 07:00:00
47 | 23-Jan-19 08:00:00
0 | 23-Jan-19 09:00:00
0 | 23-Jan-19 10:00:00
0 | 23-Jan-19 11:00:00
0 | 23-Jan-19 12:00:00
0 | 23-Jan-19 13:00:00
156 | 23-Jan-19 14:00:00
558 | 23-Jan-19 15:00:00
--......
0 | 24-Jan-19 00:00:00
0 | 24-Jan-19 01:00:00
0 | 24-Jan-19 02:00:00
--and so on

To fill the holes in the transaction hours you create first a complete table of hours.
You may use Recursive Subquery Factoring to do it
WITH hour_table(TRANSACTIONDATE) AS (
SELECT to_date('23-JAN-19 07:00:00','dd-MON-yy hh24:mi:ss') /* init hour here */
FROM DUAL
UNION ALL
SELECT TRANSACTIONDATE + 1/24
FROM hour_table
WHERE TRANSACTIONDATE + 1/24 < to_date('24-JAN-19 06:59:59','dd-MON-yy hh24:mi:ss') /* limit here */
)
select * from hour_table;
TRANSACTIONDATE
-------------------
23.01.2019 07:00:00
23.01.2019 08:00:00
...
24.01.2019 05:00:00
24.01.2019 06:00:00
Note that you use the staring and ending date in this query, the starting date must be exact an hour.
Next step is as simple as to outer join this hour table to your aggregation and set the default value for the missing hours with NVL.
with hour_table(TRANSACTIONDATE) AS (
SELECT to_date('23-JAN-19 07:00:00','dd-MON-yy hh24:mi:ss') /* init hour here */
FROM DUAL
UNION ALL
SELECT TRANSACTIONDATE + 1/24
FROM hour_table
WHERE TRANSACTIONDATE + 1/24 < to_date('24-JAN-19 06:59:59','dd-MON-yy hh24:mi:ss') /* limit */
),
agg as (
SELECT nvl(count(*),0) AS transactioncount, trunc(date_modified, 'HH') as TRANSACTIONDATE
FROM "TABLE"
WHERE date_modified between to_date('23-JAN-19 07:00:00','dd-MON-yy hh24:mi:ss') and to_date('24-Jan-19 06:59:59','dd-MON-yy hh24:mi:ss')
group by trunc(date_modified, 'HH')
)
select t.TRANSACTIONDATE, nvl(transactioncount,0) transactioncount
from hour_table t
left outer join agg a
on t.TRANSACTIONDATE = a.TRANSACTIONDATE
order by 1;

You might consider using the following with CONNECT BY level logic :
SELECT sum(transactioncount) as transactioncount, transactiondate
FROM
(
with "TABLE"(date_modified) as
(
SELECT timestamp'2019-01-23 08:00:00' FROM dual union all
SELECT timestamp'2019-01-23 08:30:00' FROM dual union all
SELECT timestamp'2019-01-23 09:00:00' FROM dual union all
SELECT timestamp'2019-01-24 05:01:00' FROM dual
)
SELECT nvl(count(*),0) AS transactioncount, trunc(date_modified, 'hh24') as transactiondate
FROM "TABLE" t
GROUP BY trunc(date_modified, 'HH24')
UNION ALL
SELECT 0, timestamp'2019-01-23 07:00:00' + ( level - 1 )/24
FROM dual
CONNECT BY level <= 24 * extract( day from
timestamp'2019-01-24 06:59:59'-
timestamp'2019-01-23 07:00:00') +
extract( hour from
timestamp'2019-01-24 06:59:59'-
timestamp'2019-01-23 07:00:00') + 1
)
GROUP BY transactiondate
ORDER BY transactiondate
Rextester Demo

Related

Find peaks of data

So I have a table Integrations.
Inte
Start Date
End Date
Total_Duration
INT1
1/7/2021 7:16:00
1/7/2021 9:22:00
02:06:00
INt2
2/7/2021 3:48:00
2/7/2021 5:10:00
01:22:00
Output I need:
Running Time
No of Inte.
1/7/2021 7:00:00
1
1/7/2021 8:00:00
1
1/7/2021 9:00:00
1
2/7/2021 4:00:00
1
2/7/2021 5:00:00
1
Basically it want to plot the peak hour when most Integrations were running.
Sql query I wrote:
select time, sum(value) as No_of_Inte
from(
select round(Start_Date, 'HH24') as time, count(*) as value
from Integrations
group by Start_Date
)
group by time
order by time asc
But this does not consider Total Duration.
Output :
Running Time
No of Inte.
1/7/2021 7:00:00
1
2/7/2021 4:00:00
1
Also, new Integrations are added every day.
This can be done using a recursive query. First create the test data
CREATE TABLE integrations (inte,start_date, end_date)
AS
(
SELECT 'INT1', TO_DATE('1/7/2021 7:16:00','DD/MM/YYYY HH24:MI:SS'), TO_DATE('1/7/2021 9:22:00','DD/MM/YYYY HH24:MI:SS') FROM dual UNION ALL
SELECT 'INT2', TO_DATE('2/7/2021 3:48:00','DD/MM/YYYY HH24:MI:SS'), TO_DATE('2/7/2021 5:10:00','DD/MM/YYYY HH24:MI:SS') FROM dual
);
Now use a recursive query to loop through the hours between start and end date. Then group by hour to get the correct counts per hour.
WITH row_per_hours (id, run_hour, end_date) AS
(
SELECT inte,
TRUNC(start_date,'HH24'),
end_date
FROM integrations
UNION ALL
SELECT id,
run_hour + INTERVAL '1' HOUR,
end_date
FROM row_per_hours
WHERE run_hour + INTERVAL '1' HOUR < end_date
)
SELECT TO_CHAR(run_hour,'DD/MM/YYYY HH24:MI:SS') as running_time,
COUNT(id) as integration_count
FROM row_per_hours
GROUP BY TO_CHAR(run_hour,'DD/MM/YYYY HH24:MI:SS') ORDER BY 1;
RUNNING_TIME INTEGRATION_COUNT
------------------- -----------------
01/07/2021 07:00:00 1
01/07/2021 08:00:00 1
01/07/2021 09:00:00 1
02/07/2021 03:00:00 1
02/07/2021 04:00:00 1
02/07/2021 05:00:00 1
For 12C and above:
You may use lateral join to generate required number of rows per each interval. Since it looks like you need some rounding of dates towards neares hour, I've added round instead of trunc. Or is there any other reason for the first interval is treating 7:00 as inclusion?.
with a(Inte, start_dt, end_dt) as (
select
'INT1'
, to_date('1/7/2021 07:16:00', 'dd/mm/yyyy hh24:mi:ss')
, to_date('1/7/2021 09:22:00', 'dd/mm/yyyy hh24:mi:ss')
from dual union all
select
'INt2'
, to_date('2/7/2021 03:48:00', 'dd/mm/yyyy hh24:mi:ss')
, to_date('2/7/2021 05:10:00', 'dd/mm/yyyy hh24:mi:ss')
from dual
)
select /*+ gather_plan_statistics */
b.hour_
, count(1) as int_cnt
from a
outer apply (
select
round(a.start_dt + numtodsinterval(level - 1, 'HOUR'), 'hh24') as hour_
from dual
connect by round(start_dt, 'hh24') + numtodsinterval(level - 1, 'HOUR') <= trunc(end_dt, 'hh24')
) b
group by b.hour_
order by 1
HOUR_ | INT_CNT
:------------------ | ------:
2021-07-01 07:00:00 | 1
2021-07-01 08:00:00 | 1
2021-07-01 09:00:00 | 1
2021-07-02 04:00:00 | 1
2021-07-02 05:00:00 | 1
db<>fiddle here

ORACLE SQL: Create new row on the basis of date range

For example, I am having a table name test_cross_months and the data is as below :
id
start_date
end_date
44
2020-01-04
2020-01-04
44
2020-01-30
2020-02-10
44
2020-02-27
2020-03-03
Expected result:
id
start_date
end_date
44
2020-01-04
2020-01-04
44
2020-01-30
2020-01-31
44
2020-02-01
2020-02-10
44
2020-02-27
2020-02-29
44
2020-03-01
2020-03-03
So for
|44|2020-01-30 |2020-02-10|
there should be two rows that are from 30-Jan-2020 to 31-Jan-2020 and 1-Feb-2020 to 10-Feb-2020
I tried by comparing the end date with the last day for the start_date but facing issues as a new row is not getting created for the end_date range.
Could any please suggest a solution?
You can use a recursive query (which will work regardless of how many months your ranges span):
WITH months ( id, start_date, end_date, final_date ) AS (
SELECT id,
start_date,
LEAST( LAST_DAY( start_date ), end_date ),
end_date
FROM table_name
UNION ALL
SELECT id,
end_date + INTERVAL '1' DAY,
LEAST( ADD_MONTHS( end_date, 1 ), final_date ),
final_date
FROM months
WHERE end_date < final_date
)
SEARCH DEPTH FIRST BY final_date SET dt_order
SELECT id,
start_date,
end_date
FROM months;
Which, for the sample data:
CREATE TABLE table_name (id, start_date, end_date) AS
SELECT 44, DATE '2020-01-04', DATE '2020-01-04' FROM DUAL UNION ALL
SELECT 44, DATE '2020-01-30', DATE '2020-02-10' FROM DUAL UNION ALL
SELECT 44, DATE '2020-02-27', DATE '2020-03-03' FROM DUAL;
Outputs:
ID
START_DATE
END_DATE
44
2020-01-04 00:00:00
2020-01-04 00:00:00
44
2020-01-30 00:00:00
2020-01-31 00:00:00
44
2020-02-01 00:00:00
2020-02-10 00:00:00
44
2020-02-27 00:00:00
2020-02-29 00:00:00
44
2020-03-01 00:00:00
2020-03-03 00:00:00
db<>fiddle here
Using a table of numbers and date arithmetic
-- example of table of numbers
with nmbrs(n) as(
select 0 from dual union all
select 1 from dual union all
select 2 from dual
)
select t.id,
case when n=0 then t.start_date else trunc(t.start_date, 'MM') + NUMTOYMINTERVAL(n, 'MONTH') end s,
case when n=MONTHS_BETWEEN(last_day(t.end_date), last_day(t.start_date)) then t.end_date
else last_day(t.start_date + NUMTOYMINTERVAL(n, 'MONTH')) end e
from test_cross_months t
join nmbrs on nmbrs.n <= MONTHS_BETWEEN(last_day(t.end_date), last_day(t.start_date))
order by t.id, s
db<>fiddle

SQL statement to return the Min and Max amount of stock per article for a given Month

I have a table from which I am trying to return the quantity per day that the article was in the system.
Example is in table Bestand the are multiple palletes of a different articles that each have a Booking In and Out date; I am try to find out the Min and Max amount of stock that was in the system per article and month.
My thinking is that if I can return the stock quantity for each day and then read out the Min and Max values.
The Timespan would be set at the time of running the SQL and the articles would be fixed.
To find out the quantity for each day I have used the following SQL:
SELECT DISTINCT
a.artbez1 AS Artikelbezeichnung,
b.artikelnr AS Artikelnummer,
SUM(CASE WHEN TO_DATE('2019-11-01 00:00:00', 'YYYY-MM-DD HH24:MI:SS') BETWEEN b.neu_datum AND b.aender_datum THEN 1 * b.menge_ist ELSE 0 END) AS "01 Nov 2019"
FROM
artikel a, bestand b
WHERE
b.artikelnr IN ('273632002', .... (huge long list of numbers) ....)
AND b.artikelnr = a.artikelnr
GROUP BY
a.artbez1, b.artikelnr;
This returns for example:
ARTIKELBEZEICHNUNG
ARTIKELNUMMER
01 Nov 2019
SC-4400.CW
220450002
39
S-320.FK120
220502004
0
H-595.FK120
220800004
35
AC-548.FK209
220948032
0
AS-6800.CW
221355002
20
I would like return this for each day of the Month and then from that return the Min and Max Value for each Article
I have the following SQL to return the days of a given Month and was wondering if anyone had any ideas on how they could be combined (If at all possible):
SELECT to_date('01.11.2019','dd.mm.yyyy')+LEVEL-1
FROM dual
CONNECT BY LEVEL <= TO_CHAR(LAST_DAY(to_date('01.11.2019','dd.mm.yyyy')),'DD')
DATES
2019-11-01 00:00:00
2019-11-02 00:00:00
2019-11-03 00:00:00
2019-11-04 00:00:00
2019-11-05 00:00:00
2019-11-06 00:00:00
2019-11-07 00:00:00
The result i am try to get would be something like:
ARTIKELBEZEICHNUNG
ARTIKELNUMMER
Nov 19 Min
Nov 19 Max
SC-4400.CW
220450002
5
39
S-320.FK120
220502004
0
15
H-595.FK120
220800004
2
35
AC-548.FK209
220948032
0
0
AS-6800.CW
221355002
10
20
Is this at all possible in SQL?
Thanks for taking the time to read my post.
JeRi
You can use a partitioned outer join:
WITH calendar ( day ) AS (
SELECT DATE '2019-11-01'
FROM DUAL
UNION ALL
SELECT day + INTERVAL '1' DAY
FROM calendar
WHERE day < LAST_DAY( DATE '2019-11-01' )
),
daily_totals ( artbez1, Artikelnr, Day, total_menge_ist ) AS (
SELECT MAX( ab.artbez1 ),
ab.artikelnr,
c.day,
COALESCE( SUM( ab.menge_ist ), 0 )
FROM calendar c
LEFT OUTER JOIN
( SELECT a.artikelnr,
a.artbez1,
b.neu_datum,
b.aender_datum,
b.menge_ist
FROM artikel a
LEFT JOIN bestand b
ON ( a.artikelnr = b.artikelnr )
-- WHERE b.artikelnr IN ('273632002', .... (huge long list of numbers) ....)
) ab
PARTITION BY ( ab.artikelnr, ab.artbez1 )
ON ( c.day BETWEEN ab.neu_datum AND ab.aender_datum )
GROUP BY ab.artikelnr, c.day
)
SELECT MAX( artbez1 ) AS Artikelbezeichnung,
artikelnr AS Artikelnummer,
TRUNC( day, 'MM' ) AS month,
MIN( total_menge_ist ) AS min_total_menge_ist,
MAX( total_menge_ist ) AS max_total_menge_ist
FROM daily_totals
GROUP BY artikelnr, TRUNC( day, 'MM' );
Which, for the sample data:
CREATE TABLE artikel ( artikelnr, artbez1 ) AS
SELECT 220450002, 'SC-4400.CW' FROM DUAL UNION ALL
SELECT 220502004, 'S-320.FK120' FROM DUAL UNION ALL
SELECT 220800004, 'H-595.FK120' FROM DUAL UNION ALL
SELECT 220948032, 'AC-548.FK209' FROM DUAL UNION ALL
SELECT 221355002, 'AS-6800.CW' FROM DUAL;
CREATE TABLE bestand ( artikelnr, neu_datum, aender_datum, menge_ist ) AS
SELECT 220450002, DATE '2019-10-30', DATE '2019-11-01', 20 FROM DUAL UNION ALL
SELECT 220450002, DATE '2019-11-01', DATE '2019-11-05', 19 FROM DUAL UNION ALL
SELECT 220502004, DATE '2019-11-05', DATE '2019-11-03', 5 FROM DUAL UNION ALL
SELECT 220800004, DATE '2019-11-01', DATE '2019-11-15', 35 FROM DUAL UNION ALL
SELECT 221355002, DATE '2019-10-20', DATE '2019-11-05', 5 FROM DUAL UNION ALL
SELECT 221355002, DATE '2019-10-25', DATE '2019-11-10', 5 FROM DUAL UNION ALL
SELECT 221355002, DATE '2019-10-28', DATE '2019-11-13', 5 FROM DUAL UNION ALL
SELECT 221355002, DATE '2019-10-30', DATE '2019-11-15', 5 FROM DUAL UNION ALL
SELECT 221355002, DATE '2019-11-05', DATE '2019-11-20', 5 FROM DUAL;
Outputs:
ARTIKELBEZEICHNUNG | ARTIKELNUMMER | MONTH | MIN_TOTAL_MENGE_IST | MAX_TOTAL_MENGE_IST
:----------------- | ------------: | :------------------ | ------------------: | ------------------:
SC-4400.CW | 220450002 | 2019-11-01 00:00:00 | 0 | 39
S-320.FK120 | 220502004 | 2019-11-01 00:00:00 | 0 | 0
AC-548.FK209 | 220948032 | 2019-11-01 00:00:00 | 0 | 0
H-595.FK120 | 220800004 | 2019-11-01 00:00:00 | 0 | 35
AS-6800.CW | 221355002 | 2019-11-01 00:00:00 | 0 | 25
db<>fiddle here

Time difference between manually entered time and datetimestamp field

I have a datetime field: PAYCOM_IN_TIME
I also have a manually entered field (ex: '07:00:00'): SCHEDULED_CLOCK_IN
SCHEDULED_CLOCK_IN is built using this in my query:
SELECT '07:00:00' SCHEDULED_CLOCK_IN FROM DUAL
I wish to get the time difference in MINUTES: PAYCOM_IN_TIME minus SCHEDULED_CLOCK_IN
All help is greatly appreciated!
I figured it out. I converted my hh:mm:ss text entry to a datetimestamp:
to_date(to_char(PAYCOM_DATE, 'MM-DD-YYYY') || S.CLOCK_IN, 'MM-DD-YYYY HH24:MI:SS')
Then I calculated Minutes:
(SCHEDULED_CLOCK_IN - P.CLOCKIN) * 24 * 60 DIFF_MINS
You can use:
SELECT paycom_in_time,
scheduled_clock_in,
( TRUNC( paycom_in_time ) + scheduled_clock_in - paycom_in_time ) DAY TO SECOND
AS difference_interval,
ROUND(
( TRUNC( paycom_in_time ) + scheduled_clock_in - paycom_in_time )
* 24 * 60
) AS difference_minutes
FROM table_name
CROSS JOIN (
SELECT INTERVAL '07:00' HOUR TO MINUTE AS scheduled_clock_in FROM DUAL
)
Which, for the sample data:
CREATE TABLE table_name ( paycom_in_time ) AS
SELECT DATE '2020-10-17' + INTERVAL '07:49' HOUR TO MINUTE FROM DUAL UNION ALL
SELECT DATE '2020-10-13' + INTERVAL '06:51' HOUR TO MINUTE FROM DUAL UNION ALL
SELECT DATE '2020-10-22' + INTERVAL '06:56' HOUR TO MINUTE FROM DUAL UNION ALL
SELECT DATE '2020-10-23' + INTERVAL '06:47' HOUR TO MINUTE FROM DUAL UNION ALL
SELECT DATE '2020-10-26' + INTERVAL '06:52' HOUR TO MINUTE FROM DUAL UNION ALL
SELECT DATE '2020-10-30' + INTERVAL '06:59' HOUR TO MINUTE FROM DUAL;
Outputs:
PAYCOM_IN_TIME | SCHEDULED_CLOCK_IN | DIFFERENCE_INTERVAL | DIFFERENCE_MINUTES
:------------------ | :----------------- | :------------------ | -----------------:
2020-10-17 07:49:00 | +00 07:00:00 | -00 00:49:00.000000 | -49
2020-10-13 06:51:00 | +00 07:00:00 | +00 00:09:00.000000 | 9
2020-10-22 06:56:00 | +00 07:00:00 | +00 00:04:00.000000 | 4
2020-10-23 06:47:00 | +00 07:00:00 | +00 00:13:00.000000 | 13
2020-10-26 06:52:00 | +00 07:00:00 | +00 00:08:00.000000 | 8
2020-10-30 06:59:00 | +00 07:00:00 | +00 00:01:00.000000 | 1
db<>fiddle here
Use a query with
SELECT TIME_TO_SEC(TIMEDIFF(paycom, shedulded))/60;
Or
SELECT DATEDIFF(MINUTE, paycom, shedulded) AS [Time in Minutes]

How to count ratio hourly?

I`m stuck a bit with understanding of my further actions while performing queries.
I have two tables "A"(date, response, b_id) and "B"(id, country). I need to count hourly ratio of a number of entries where response exists to the total number of entries on a specific date. The final selection should consist of columns "hour", "ratio".
SELECT COUNT(*) FROM A WHERE RESPONSE IS NOT NULL//counting entries with response
SELECT COUNT(*) FROM A//counting total number of entries
How to count the ratio? Should I create a separate variable for it?
How to count for each hour on a day? Should I make smth like a loop? + How can I get the "hour" part of a date?
What is the best way to select the hours and counted ratio? Should I make a separate table for it?
I`m rather new to make complex queries, so I woud be happy for every kind of help
You can do this as:
select to_char(datecol, 'HH24') as hour,
count(response) as has_response, count(*) as total,
count(response) / count(*) as ratio
from a
where datecol >= date '2018-09-18' and datecol < date '2018-09-19'
group by to_char(datecol, 'HH24');
You can also do this using avg() -- which is also fun:
select to_char(datecol, 'HH24'),
avg(case when response is not null then 1.0 else 0 end) as ratio
from a
where datecol >= date '2018-09-18' and datecol < date '2018-09-19'
group by to_char(datecol, 'HH24')
In this case, that requires more typing, though.
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE A ( dt, response, b_id ) AS
SELECT DATE '2018-09-18' + INTERVAL '00:00' HOUR TO MINUTE, NULL, 1 FROM DUAL UNION ALL
SELECT DATE '2018-09-18' + INTERVAL '00:10' HOUR TO MINUTE, 'A', 1 FROM DUAL UNION ALL
SELECT DATE '2018-09-18' + INTERVAL '00:20' HOUR TO MINUTE, 'B', 1 FROM DUAL UNION ALL
SELECT DATE '2018-09-18' + INTERVAL '01:00' HOUR TO MINUTE, 'C', 1 FROM DUAL UNION ALL
SELECT DATE '2018-09-18' + INTERVAL '01:10' HOUR TO MINUTE, 'D', 1 FROM DUAL UNION ALL
SELECT DATE '2018-09-18' + INTERVAL '02:00' HOUR TO MINUTE, NULL, 1 FROM DUAL UNION ALL
SELECT DATE '2018-09-18' + INTERVAL '03:00' HOUR TO MINUTE, 'E', 1 FROM DUAL UNION ALL
SELECT DATE '2018-09-18' + INTERVAL '05:10' HOUR TO MINUTE, 'F', 1 FROM DUAL;
Query 1:
SELECT b_id,
TO_CHAR( TRUNC( dt, 'HH' ), 'YYYY-MM-DD HH24:MI:SS' ) AS hour,
COUNT(RESPONSE) AS total_response_per_hour,
COUNT(*) AS total_per_hour,
total_response_per_day,
total_per_day,
COUNT(response) / total_response_per_day AS ratio_for_responses,
COUNT(*) / total_per_day AS ratio
FROM (
SELECT A.*,
COUNT(RESPONSE) OVER ( PARTITION BY b_id, TRUNC( dt ) ) AS total_response_per_day,
COUNT(*) OVER ( PARTITION BY b_id, TRUNC( dt ) ) AS total_per_day
FROM A
)
GROUP BY
b_id,
total_per_day,
total_response_per_day,
TRUNC( dt, 'HH' )
ORDER BY
TRUNC( dt, 'HH' )
Results:
| B_ID | HOUR | TOTAL_RESPONSE_PER_HOUR | TOTAL_PER_HOUR | TOTAL_RESPONSE_PER_DAY | TOTAL_PER_DAY | RATIO_FOR_RESPONSES | RATIO |
|------|---------------------|-------------------------|----------------|------------------------|---------------|---------------------|-------|
| 1 | 2018-09-18 00:00:00 | 2 | 3 | 6 | 8 | 0.3333333333333333 | 0.375 |
| 1 | 2018-09-18 01:00:00 | 2 | 2 | 6 | 8 | 0.3333333333333333 | 0.25 |
| 1 | 2018-09-18 02:00:00 | 0 | 1 | 6 | 8 | 0 | 0.125 |
| 1 | 2018-09-18 03:00:00 | 1 | 1 | 6 | 8 | 0.16666666666666666 | 0.125 |
| 1 | 2018-09-18 05:00:00 | 1 | 1 | 6 | 8 | 0.16666666666666666 | 0.125 |
SELECT withResponses.hour,
withResponses.cnt AS withResponse,
alls.cnt AS AllEntries,
(withResponses.cnt / alls.cnt) AS ratio
FROM
( SELECT to_char(d, 'DD-MM-YY - HH24') || ':00 to :59 ' hour,
count(*) AS cnt
FROM A
WHERE RESPONSE IS NOT NULL
GROUP BY to_char(d, 'DD-MM-YY - HH24') || ':00 to :59 ' ) withResponses,
( SELECT to_char(d, 'DD-MM-YY - HH24') || ':00 to :59 ' hour,
count(*) AS cnt
FROM A
GROUP BY to_char(d, 'DD-MM-YY - HH24') || ':00 to :59 ' ) alls
WHERE alls.hour = withResponses.hour ;
SQLFiddle: http://sqlfiddle.com/#!4/c09b9/2