Dynamically excluding first 5 minutes of data each hour of loading - oracle

Data gets fetched from the below "CLAIMANT" table to load into another table. This happens every hour (6 am, 7am, 8am, etc.)
Say I need to write a WHERE CLAUSE that excludes the first 5 minutes of data from being loaded, each time the above operation occurs. The where clause will be added to the overall script that handles the operation
I have tried this, but it excludes 0 records, so it's not detecting the minute by minute differences of the "Filed_Date"
where FILED_DATE not between SYSDATE
and ( TRUNC( SYSDATE, 'hh24' ) + interval '5' minute )
What is the correct way to write the where clause to exclude the data I need to exclude

Your original predicate has a start time and end time out of sync. If you run the query at 2:00 (sysdate), then the start time for your range is SYSDATE (2:00) and the end time is (TRUNC(SYSDATE,'HH24')+interval '5' minute), or 2:05. If you run the query after 14:05, then your start time will actually be after your end time.
select to_date('2021-03-03 14:00','YYYY-MM-DD HH24:MI') query_date,
to_date('2021-03-03 14:00','YYYY-MM-DD HH24:MI') start_time,
(trunc(to_date('2021-03-03 14:00','YYYY-MM-DD HH24:MI'),'HH24') + interval '5' minute) stop_time
from dual
union
select to_date('2021-03-03 14:10','YYYY-MM-DD HH24:MI') query_date,
to_date('2021-03-03 14:10','YYYY-MM-DD HH24:MI') start_time,
(trunc(to_date('2021-03-03 14:10','YYYY-MM-DD HH24:MI'),'HH24') + interval '5' minute) stop_time
from dual;
Results:
QUERY_DATE START_TIME STOP_TIME
-------------------- -------------------- --------------------
2021-03-03T14:00:00Z 2021-03-03T14:00:00Z 2021-03-03T14:05:00Z
2021-03-03T14:10:00Z 2021-03-03T14:10:00Z 2021-03-03T14:05:00Z
What you want is the data from 13:05 to 14:00, but the only data you are excluding is data you haven't received yet (after 14:00), so you get everything up to 14:00, or no data at all because of the mismatched start and stop times of your interval.
Try this, assuming the query runs on or after the top of the hour and you want to capture everything after the first five minutes of the previous hour up to the top of the current hour (i.e. the last 55 minutes of the previous hour):
where filed_date
between (trunc(sysdate,'HH24') - interval '55' minute) -- start time
and (trunc(sysdate,'HH24')) -- end time
Now if the job time (sysdate) is any time between 14:00 and 14:59, then start time for the predicate is always 13:05 and stop time is always 14:00.
select to_date('2021-03-03 14:00','YYYY-MM-DD HH24:MI') query_date,
(trunc(to_date('2021-03-03 14:00','YYYY-MM-DD HH24:MI'),'HH24') - interval '55' minute) start_time,
(trunc(to_date('2021-03-03 14:00','YYYY-MM-DD HH24:MI'),'HH24')) stop_time
from dual
union
select to_date('2021-03-03 14:10','YYYY-MM-DD HH24:MI') query_date,
(trunc(to_date('2021-03-03 14:10','YYYY-MM-DD HH24:MI'),'HH24') - interval '55' minute) start_time,
(trunc(to_date('2021-03-03 14:10','YYYY-MM-DD HH24:MI'),'HH24')) stop_time
from dual;
Results:
QUERY_DATE START_TIME STOP_TIME
-------------------- -------------------- --------------------
2021-03-03T14:00:00Z 2021-03-03T13:05:00Z 2021-03-03T14:00:00Z
2021-03-03T14:10:00Z 2021-03-03T13:05:00Z 2021-03-03T14:00:00Z
In general it is better to stay with positive assertions (i.e. BETWEEN); negative conditions (i.e. NOT BETWEEN) tend to discourage the CBO from using indexes, if they exist.
Alternatively, if you ever wanted a larger timeframe and always wanted to exclude the first five minutes of any hour that happened to fall in the range, you could do this:
where filed_date
between (sysdate - interval '3' hour) and sysdate
and to_number(to_char(filed_date,'MI'),'99') > 5
This would limit the minutes portion of filed_date to 6 or more, effectively filtering out the first five minutes of any hour in the range.

Related

Time in oracle with crossing over 0:00

can you help me please.
I'm use:
round((24 * 60 * (to_date(EndPick, 'HH24:MI') - to_date(StartPick, 'HH24:MI'))), 2) as WorkTime
All time EndPick bigger then StartPick. But we have a chance, what Start bigger where
estimate time interval if there is a crossing over 00:00 into the next date?
In relult for example:
3
(minutes)
But we have a chance, what Start bigger where estimate time interval if there is a crossing over 00:00 into the next date?
Use a CASE expression and add a day if startpick > endpick:
SELECT round(
(24 * 60 * (to_date(EndPick, 'HH24:MI')
+ CASE
WHEN startpick > endpick
THEN INTERVAL '1' DAY
ELSE INTERVAL '0' DAY
END
- to_date(StartPick, 'HH24:MI')
)),
2
) as WorkTime
FROM table_name;
Which, for the sample data:
CREATE TABLE table_name (startpick, endpick) AS
SELECT '00:00', '01:00' FROM DUAL UNION ALL
SELECT '23:00', '00:00' FROM DUAL;
Outputs:
WORKTIME
60
60
fiddle

Oracle: splitting time range into days and calculation of duration

I'm developing code calculation service availability based on events, so I need to split events into daily "sub-events" and calculate duration of then.
So as input I have set of events like (EVENT_ID, START_TIME, END_TIME):
'event1';2021-05-01 12:30;2021-05-01 13:00
'event2';2021-05-03 10:55;2021-05-05 12:01
As output I'd like to get (EVENT_ID, DAY, DURATION_MINUTES):
'event1'; 2021-05-01; 30
'event2'; 2021-05-03; 785
'event2'; 2021-05-04; 1440
'event2'; 2021-05-05; 721
I can get it using procedures and cursor but this is not effective (the events database is quite big), so is there a way to do it using oracle sql query ? Any idea?
You appear to want a recursive query:
WITH days ( event_id, day, start_time, end_time ) AS (
SELECT event_id,
TRUNC( start_time ),
start_time,
end_time
FROM table_name
UNION ALL
SELECT event_id,
day + INTERVAL '1' DAY,
start_time,
end_time
FROM days
WHERE day + INTERVAL '1' DAY < end_time
)
SELECT event_id,
day,
ROUND(
(
LEAST(end_time, day + INTERVAL '1' DAY)
- GREATEST(start_time, day)
) * 24 * 60
) AS duration_minutes
FROM days
Which, for the sample data:
CREATE TABLE table_name ( event_id, start_time, end_time ) AS
SELECT 'event1', DATE '2021-05-01' + INTERVAL '12:30' HOUR TO MINUTE, DATE '2021-05-01' + INTERVAL '13:00' HOUR TO MINUTE FROM DUAL UNION ALL
SELECT 'event2', DATE '2021-05-03' + INTERVAL '10:55' HOUR TO MINUTE, DATE '2021-05-05' + INTERVAL '12:01' HOUR TO MINUTE FROM DUAL;
Outputs:
EVENT_ID
DAY
DURATION_MINUTES
event1
2021-05-01
30
event2
2021-05-03
785
event2
2021-05-04
1440
event2
2021-05-05
721
db<>fiddle here
If your Oracle version is 12 or higher, you can use a lateral join (in any of several equivalent formulations/syntaxes) to make the query faster. For example (using the table set up in MT0's answer):
select event_id, day, round(1440 * duration_days) as duration_minutes
from table_name cross join lateral
( select trunc(start_time) + level - 1 as day,
case when level = 1 and connect_by_isleaf = 1
then end_time - start_time
when level = 1 then 1 - (start_time - trunc(start_time))
when connect_by_isleaf = 1 then end_time - trunc(end_time)
else 1 end as duration_days
from dual
connect by level <= 1 + trunc(end_time) - trunc(start_time)
)
where duration_days != 0
order by event_id, day
;
The where clause is used when the end_time is midnight (at the beginning of an otherwise "new" day); in that case, presumably, you don't want to include that "new day" in the output, with a duration of 0 minutes.
In the lateral view, level = 1 corresponds to the first date in the interval, while connect_by_isleaf = 1 is for the last date in the interval. A special calculation is made when the end_time and start_time are on the same date. The query computes the difference in days first, then converts to minutes. Note that date calculations aren't 100% precise; I used round so I don't get results like 33.9999999999938020 minutes. If the inputs are in hh24:mi, we know beforehand that the answer (in minutes) should be an integer, so round seems fine there.

Selecting every 3 seconds data rows between 2 dates

I have a requirement to take every 3 seconds data within the specific time interval in SQL. I am new to SQL so can anyone help me on the scenario
This is my select query which returns all the values but i need data for every 3 seconds only
SELECT ton_nbr
FROM
icr_file_interface
WHERE
(
reading_dttm BETWEEN
TO_DATE(concat('2016-10-19',to_char(0930)),'yyyy-mm-dd HH24MISS')
AND TO_DATE(concat('2016-10-19',to_char('0945')),'yyyy-mm-dd HH24MISS')
)
AND
(
ton_nbr BETWEEN
(SELECT value FROM text_para WHERE para_cd='ICR_ST_RNG')
AND (SELECT value FROM text_para WHERE para_cd='ICR_ED_RNG')
)
If you only need to subtract 3 seconds from a date, you can use the following:
SQL> select to_char(sysdate, 'yyyy-mm-dd hh24:mi:ss'),
2 to_char(sysdate - 3*1/24/60/60, 'yyyy-mm-dd hh24:mi:ss')
3 from dual;
TO_CHAR(SYSDATE,'YY TO_CHAR(SYSDATE-3*1
------------------- -------------------
2016-10-19 09:38:17 2016-10-19 09:38:14
Given that sysdate -1 means "subtract one day to sysdate", you can derive the number of seconds you need with a bit af arithmetic
This selects you data between last 3 seconds. Hope you got the idea.
select ton_nbr
from icr_file_interface
where reading_dttm between dateadd(ss, -3, getdate()) and getdate() ;

Wrong difference in minutes in Oracle (DATE2-DATE1)*24*60

I'm trying to calculate difference in minutes between two dates in Oracle with this testing query:
SELECT
(DATE2-DATE1)*24*60 DIFFINMINUTES
FROM
(
SELECT
TO_DATE('2014-06-06 10:30:00', 'YYYY-MM-DD HH24:MI:SS') DATE1,
TO_DATE('2014-06-06 11:25', 'YYYY-MM-DD HH24:MI') DATE2
FROM DUAL
);
The expected result should be 55 minutes but I'm getting 54,99999999.
ROUNDing this value gets the job done but I really wants to understand why this calculation was this behavior.
DATE2 - DATE1 Oracle calculates the difference in days. That's why some inaccuracy may occur.
Just an example with timestamps
select inter,
extract (day from inter) days,
extract (hour from inter) hours,
extract (minute from inter) minutes,
extract (second from inter) seconds
from
(select
cast(date2 as timestamp) - cast(date1 as timestamp) inter
FROM
(
SELECT
TO_DATE('2014-06-06 10:30:00', 'YYYY-MM-DD HH24:MI:SS') DATE1,
to_date('2014-06-06 11:25', 'YYYY-MM-DD HH24:MI') date2
from dual
));
Substraction of timestamps gives you exact INTERVAL

How to “group by” over a DATETIME range?

I'm trying to bulid up a datetime range based transactions report, for a business that can be open across two days, depending on the shift management.
The user can select a datetime range (monthly, daily, weekly, freely...), the query I implemented get the startDateTime and the EndDateTime, and will return all the transactions total grouped by day.
I.E.
DateTime Total Sales
---------------------------
10/15/2010 $2,300.38
10/16/2010 $1,780.00
10/17/2010 $4,200.22
10/20/2010 $900.66
My problem is that if the shift of the business is setted, for example, from 05.00 AM to 02.00 AM of the next day, all the transactions done from midnight to 02.00 AM will be grouped in the next day... and so on... the totals are corrupted.
When a business has a shift like this, it wants a report based on that shift, but without code patching (I'm using Java calling Oracle native queries), I'm unable to get the requested report.
I'm wondering if there is some smart manner to group by a datetime range these sets of transactions using nothing more than Oracle.
Here goes the query, for the the month of July:
SELECT Q1.dateFormat, NVL(Q1.sales, 0)
FROM (
SELECT to_date(to_char(tx.datetimeGMT +1/24 , 'mm-dd-yyyy'), 'mm-dd-yyyy') AS dateFormat
, NVL(SUM(tx.amount),0) AS sales
FROM Transaction tx
WHERE tx.datetimeGMT > to_date('20100801 08:59:59', 'yyyymmdd hh24:mi:ss') +1/24
AND tx.datetimeGMT < to_date('20100901 09:00:00', 'yyyymmdd hh24:mi:ss') + 1/24
GROUP BY to_date(to_char(tx.datetimeGMT +1/24 , 'mm-dd-yyyy'), 'mm-dd-yyyy')
) Q1
ORDER BY 1 DESC
Thank you all for your answers, by taking a look to them I could write down the query I was searching for:
SELECT CASE
WHEN EXTRACT(HOUR FROM TX.DATETIME) >= 5 THEN TO_CHAR(TX.DATETIME,'DD-MM-YYYY')
WHEN EXTRACT(HOUR FROM TX.DATETIME) BETWEEN 0 AND 2 THEN TO_CHAR(TX.DATETIME-1,'DD-MM-YYYY')
WHEN EXTRACT(hour from tx.datetime) between 2 and 5 THEN to_char(TX.DATETIME-1,'DD-MM-YYYY')
END AS age,
NVL(SUM(tx.amount),0) AS sales
FROM TRANSACTION TX
WHERE tx.datetime > to_date('20100801 08:59:59', 'yyyymmdd hh24:mi:ss')
AND TX.DATETIME < TO_DATE('20100901 09:00:00', 'yyyymmdd hh24:mi:ss')
GROUP BY CASE
WHEN EXTRACT(HOUR FROM TX.DATETIME) >= 5 THEN TO_CHAR(TX.DATETIME,'DD-MM-YYYY')
WHEN EXTRACT(HOUR FROM TX.DATETIME) BETWEEN 0 AND 2 THEN TO_CHAR(TX.DATETIME-1,'DD-MM-YYYY')
WHEN EXTRACT(hour from tx.datetime) between 2 and 5 THEN to_char(TX.DATETIME-1,'DD-MM-YYYY')
END
ORDER BY 1
To group by a date range, you'll have to have this range into a column value into a subquery, and group by it in your query. Obviously, this date range within this column value will be of VARCHAR type.
If the first shift of the day starts at 08:00, and the last shift of that same day ends 07:59 the next day, you can use something like this to group the transactions by the shift date.
select trunc(trans_date - interval '8' hour) as shift_date
,sum(amount)
from transactions
group
by trunc(trans_date - interval '8' hour)
order
by shift_date desc;
You can try this approach (just out of my head, not even sure if it runs):
select
trans_date,
trans_shift,
aggregates(whatever)
from (
select
-- we want to group by normalized transaction date,
-- not by real transaction date
normalized_trans_date,
-- get the shift to group by
case
when trans_date between trunc(normalized_trans_date) + shift_1_start_offset and
trunc(normalized_trans_date) + shift_1_end_offset then
1
when trans_date between trunc(normalized_trans_date) + shift_2_start_offset and
trunc(normalized_trans_date) + shift_2_end_offset then
2
...
when trans_date between trunc(normalized_trans_date) + shift_N_start_offset and
trunc(normalized_trans_date) + shift_N_end_offset then
N
end trans_shift,
whatever
from (
select
-- get a normalized transaction date: if date is before 1st shift
-- it belongs to the day before
case
when trans_date - trunc(trans_date) < shift_1_start_offset then
trans_date - 1
else
trans_date
end normalized_trans_date,
t.*
from
transactions t
)
)
group by trans_date, trans_shift
Ronnis solution with the trunc(trans_date - interval '8' hour) helped me in a similar query.
Did a Backup Report and had to summarize output-bytes from RC_BACKUP_SET_DETAILS. The backup task runs for more than 8 hours, there are several RC_BACKUP_SET_DETAILS rows for one job which starts at night time and end the next day.
select trunc(start_time - interval '12' hour) "Start Date",
to_char(sum(output_bytes)/(1024*1024*1024),'999,990.0') "Output GB"
from rc_backup_set_details
where db_key = 173916 and backup_type = 'I' and incremental_level = 0
group by trunc(start_time - interval '12' hour)
order by 1 asc;

Resources