Divide time period into partitions and calculate averages - oracle

I have to divide elapsed time of a process into slots of equal sizes and calculate number of rows inserted during each time slot.
Here is DDL of the table
ID number
insert_date_time date
Sample Data
101 Aug 1 2015 4:43:00 PM
931 Aug 1 2015 4:43:01 PM
The output I am looking for is as follows
Time Slot Rows Inserted
4:00 pm - 5:00 pm 103
5:00 pm - 6:00 pm 95
6:00 pm - 7:00 pm 643
( I have left out date portion for brevity )
Similarly, I have to find out how much time each 100 rows took
0 - 100 rows 4:00pm - 4:43pm
101 - 200 rows 4:43pm - 5:58pm
I know Oracle OLAP functions can be used for this, but not sure how ?

Hoping I understood your requirement.
First Req#:
SELECT TO_CHAR(INSERT_TIME,'HH24'),COUNT(*) FROM TABLE
GROUP BY TO_CHAR(INSERT_TIME,'HH24');
Second Req#:
SELECT ROUND((ROW_NUMBER-1)/100,0),
MIN(INSERT_TIME) +'-'+ MAX(INSERT_TIME)
FROM TABLE
GROUP BY ROUND((ROW_NUMBER-1)/100,0);

If you are looking for 1 hour partitions, you can simply truncate the date column you are looking at to the hour trunc(your_date_col, 'hour') using that truncated value in your grouping clause.
For the 2nd requirement you can partition your rows with a query like this:
with data as (
select a.your_date_col
, row_number() over (order by Your_Date_Col) rn
, row_number() over (order by Your_Date_Col) -
mod(row_number() over (order by Your_Date_Col)-1,50) grp
from your_table a
)
select min(rn) ||' - '
||max(rn) ||' rows' rows
, min(your_date_col)||' - '
||max(your_date_col) time_range
from data
group by grp

Related

Where clause from a subquery

I have a table with business days BUSINESS_DAYS which has all the dates
I have another table with payment information and DUE_DATES
I want to return in my query the next business day IF the DUE_DATE is not a business day
SELECT SQ1.DUE_DATE, SQ2.DATE FROM
(SELECT * FROM
PAYMENTS
ORDER BY
DUE_DATE) SQ1,
(SELECT MIN(DATE) DATE FROM BUSINESS_DAYS WHERE SQ1.DUE_DATE <= DATE GROUP BY DATE) SQ2
Anyone can shed some light?
The way I see it, code you posted doesn't do what you wanted anyway (otherwise, you won't be asking a question at all). Therefore, I'd suggest another approach:
Altering the session (you don't have to do it; my database speaks Croatian so I'm switching to English; also, setting date format to display day name):
SQL> alter session set nls_date_language = 'english';
Session altered.
SQL> alter session set nls_date_format = 'dd.mm.yyyy, dy';
Session altered.
Two CTEs contain
business_days: as commented, only this year's July, weekends excluded, there are no holidays)
payments: two rows, one whose due date is a working day and another whose isn't
Sample data end at line #15, query you might be interested in begins at line #16. Its CASE expression check whether due_date is one of weekend days; if not, due date to be returned is exactly what it is. Otherwise, another SELECT statement returns the first (MIN) business day larger than due_date.
SQL> with
2 business_days (datum) as
3 -- for simplicity, only all dates in this year's July,
4 -- weekends excluded (as they aren't business days), no holidays
5 (select date '2021-07-01' + level - 1
6 from dual
7 where to_char(date '2021-07-01' + level - 1, 'dy')
8 not in ('sat', 'sun')
9 connect by level <= 31
10 ),
11 payments (id, due_date) as
12 (select 1, date '2021-07-14' from dual -- Wednesday, business day
13 union all
14 select 2, date '2021-07-25' from dual -- Sunday, non-business day
15 )
16 select p.id,
17 p.due_date current_due_date,
18 --
19 case when to_char(p.due_date, 'dy') not in ('sat', 'sun') then
20 p.due_date
21 else (select min(b.datum)
22 from business_days b
23 where b.datum > p.due_date
24 )
25 end new_due_date
26 from payments p
27 order by id;
ID CURRENT_DUE_DAT NEW_DUE_DATE
---------- --------------- ---------------
1 14.07.2021, wed 14.07.2021, wed --> Wednesday remains "as is"
2 25.07.2021, sun 26.07.2021, mon --> Sunday switched to Monday
SQL>

Max number of counts in a tparticular hour

I have a table called Orders, i want to get maximum number of orders for each day with respect to hours with following query
SELECT
trunc(created,'HH') as dated,
count(*) as Counts
FROM
orders
WHERE
created > trunc(SYSDATE -2)
group by trunc(created,'HH') ORDER BY counts DESC
this gets the result of all hours, I want only max hour of a day e.g.
Image
This result looks good but now i want only rows with max number of count for a day
e.g.
for 12/23/2019 max number of counts is 90 for "12/23/2019 4:00:00 PM",
for 12/22/2019 max number of counts is 25 for "12/22/2019 3:00:00 PM"
required dataset
1 12/23/2019 4:00:00 PM 90
2 12/24/2019 12:00:00 PM 76
3 12/22/2019 1:00:00 PM 25
This could be the solution and in my opinion is the most trivial.
Use the WITH clause to make a sub query then search for the greatest value in the data set on a specific date.
WITH ORD AS (
SELECT
trunc(created,'HH') as dated,
count(*) as Counts
FROM
orders
WHERE
created > trunc(SYSDATE-2)
group by trunc(created,'HH')
)
SELECT *
FROM ORD ord
WHERE NOT EXISTS (
SELECT 'X'
FROM ORD ord1
WHERE trunc(ord1.dated) = trunc(ord.dated) AND ord1.Counts > ord.Counts
)
Use ROW_NUMBER analytic function over your original query and filter the rows with number 1.
You need to partition on the day, i.e. TRUNC(dated) to get the correct result
with ord1 as (
SELECT
trunc(created,'HH') as dated,
count(*) as Counts
FROM
orders
WHERE
created > trunc(SYSDATE -2)
group by trunc(created,'HH')
),
ord2 as (
select dated, Counts,
row_number() over (partition by trunc(dated) order by Counts desc) as rn
from ord1)
select dated, Counts
from ord2
where rn = 1
The advantage of using the ROW_NUMBER is that it correct handels ties, i.e. cases where there are more hour in a day with the same maximal count. The query shows only one record and you can controll with the order by e.g. to show the first / last hour.
You can use the analytical function ROW_NUMBER as following to get the desired result:
SELECT DATED, COUNTS
FROM (
SELECT
TRUNC(CREATED, 'HH') AS DATED,
COUNT(*) AS COUNTS,
ROW_NUMBER() OVER(
PARTITION BY TRUNC(CREATED)
ORDER BY COUNT(*) DESC NULLS LAST
) AS RN
FROM ORDERS
WHERE CREATED > TRUNC(SYSDATE - 2)
GROUP BY TRUNC(CREATED, 'HH'), TRUNC(CREATED)
)
WHERE RN = 1
Cheers!!

Procedure with cursor in order to populate the table using view incrementally

view:
po po_line month year amount
41216 10 jan 2018 3000
41216 20 feb 2018 4000
41216 30 Aug 2018 6000
54321 10 march 2018 7000
32133 10 feb 2018 5000
Table:
po po_line month year amount
41216 10 jan 2018 3000
41216 20 feb 2018 4000
I need a procedure with a cursor in order to populate the table using view incrementally.
The idea is that when the view gets updated the updated data should be inserted in the table.
code:
create or replace procedure prc as
cursor c1 is
select *
from vw_po_tab
where po||po_line not in(select po||po_line from po_tab1);
begin
for i in c1 loop
insert into po_tab1(po,po_line,month,year, amount)
values(i.po,i.po_line,i.month,i.year, i.amount);
end loop;
end;
You don't need a loop (or even a procedure). You can do that with one INSERT statement:
insert into po_tab1(po,po_line,month,year, amount)
select po, po_line, month, year, amount
from vw_po_tab
where (po, po_line) not in (select po, po_line
from po_tab1);
For a multi-column IN condition you shouldn't concatenate the two values, but use two separate columns. Because 1,12 and 11,2 would be the same if you concatenate them.

Group by hour using Oracle SQL

I have a varchar5 column with times from
00:00
00:01
00:02
00:03
... all the way to
23:59
How would I count how many minutes are in an hour? To get the result
00 60
01 60
02 60
and so on...
SQL:
select 24_HOUR_CLOCK
From time table
Group by ...
Order by 24_HOUR_CLOCK ASC
Means to count records
I think you can use substr to extract first two characters from the time string and group on that.
select substr(col, 1, 2) hour, count(*) minutes
from your_table
group by substr(col, 1, 2)
order by hour
or find substr inside a subquery as #Mathguy suggested:
select hour,
count(*) minutes
from (
select substr(col, 1, 2) hour
from your_table
)
group by hour
order by hour

How to find the difference between two timestamp column

I have table name Record which has the following columns, Empid in number column, dat in timestamp
Which has the following values
empid dat
====== ====
101 4/9/2012 9:48:54 AM
101 4/9/2012 9:36:28 AM
101 4/9/2012 6:16:28 PM
101 4/10/2012 9:33:48 AM
101 4/10/2012 12:36:28 PM
101 4/10/2012 8:36:12 PM
101 4/11/2012 9:36:28 AM
101 4/11/2012 4:36:22 PM
Here I need to display the following columns,
empid,min(dat) as start,max(dat) as end and difference(max(dat)-min(dat) for each day
Here 3 different days are exists so It should return 3 records with the above mentioned columns.
Please give some ways to get this.
Simply subtract them: max(dat) - min(dat)
SELECT empid,
min(dat) as strt,
max(dat) as end,
max(dat) - min(dat) as diff
FROM the_table
GROUP BY empid;
If you want to group by the day instead of the empid, use this one:
select trunc(dat),
min(dat) as strt,
max(dat) as end,
max(dat) - min(dat) as diff
from the_table
group by trunc(dat)
Date arithmetic is pretty straightforward in Oracle: the difference betwwen two dates is returned as the number of days. Values of less than a day are returned as *decimals". That is, 75 minutes is 1.25 hours not 1.15. If you want it as hours and minutes you need to work with an interval.
The inner query calculates the difference between the minimum and maximum data for each employee for each day, and converts it to a DAY interval. The outer query extracts the HOUR and MINUTES from that interval.
select empid
, tday
, sdt
, edt
, extract(hour from diff) diff_hours
, extract (minute from diff) diff_minutes
from (
select empid
, trunc(dat) tday
, min(dat) sdt
, max(dat) edt
, numtodsinterval(max(dat) - min(dat), 'DAY') diff
from t42
group by empid, trunc(dat)
)

Resources