Oracle INCREMENT fractional parts of a timestamp - oracle

I have a table, which contains a timestamp. I want each row an INTERVAL apart. In my example below I am using a15 minutes interval
My first solution appears to work perfectly except that the fractional part of the timestamp is always .000000, which I expect but don't want. I'd like it to contain some other numbers.
In my second attempt I'm trying to INCREMENT the fractional part of the timestamp by .100000 the problem with this solution is if I'm creating many rows (1344) in my example, the seconds part of the timestamp gets incremented by 1 second after 10 rows are inserted. See the second solution below. I don't want that either.
Thirdly, I thought perhaps a regexp_replace solution would work where I could chop off the integer part of the second solution (keep the fractional part) and then add that to my interval. That attempt failed with an error. See third attempt below.
Is there a way I can get this to work? Where I can change the fractional part of the timestamp without it affecting the MMDDYYYY HH24:MI:SS part of the date.
Below is my code and attempts along with an example of the sample output I'm looking to generate.
Attempt #1 fractional part always .000000
CREATE TABLE t3 (
seq_num NUMBER GENERATED BY DEFAULT AS IDENTITY (START WITH 1) NOT NULL,
dt TIMESTAMP );
/
INSERT into t3 (dt)
with dt (dt, interv) as (
select timestamp '2022-01-01 00:00:00',
numtodsinterval(15,'MINUTE') from dual
union all
select dt.dt + interv, interv from dt
where dt.dt + interv < date '2022-01-15')
select dt from dt;
/
SELECT * FROM T3 ORDER BY SEQ_NUM
SEQ_NUM DT
1 01-JAN-22 12.00.00.000000 AM
2 01-JAN-22 12.15.00.000000 AM
3 01-JAN-22 12.30.00.000000 AM
4 01-JAN-22 12.45.00.000000 AM
5 01-JAN-22 01.00.00.000000 AM
6 01-JAN-22 01.15.00.000000 AM
…
...
1342 14-JAN-22 11.15.00.000000 PM
1343 14-JAN-22 11.30.00.000000 PM
1344 14-JAN-22 11.45.00.000000 PM
Attempt #2 notice the seconds change at seq_num 1355. It went from :00 to :01
TRUNCATE TABLE T3;
/
INSERT into t3 (dt)
with dt (dt, interv) as (
select timestamp '2022-01-01 00:00:00',
numtodsinterval(15,'MINUTE') +
numtodsinterval( (rownum * .100000), 'SECOND') from dual
union all
select dt.dt + interv, interv from dt
where dt.dt + interv < date '2022-01-15')
select dt from dt;
/
SELECT * FROM T3 ORDER BY SEQ_NUM
SEQ_NUM DT
1345 01-JAN-22 12.00.00.000000 AM
1346 01-JAN-22 12.15.00.100000 AM
1347 01-JAN-22 12.30.00.200000 AM
1348 01-JAN-22 12.45.00.300000 AM
1349 01-JAN-22 01.00.00.400000 AM
1350 01-JAN-22 01.15.00.500000 AM
1351 01-JAN-22 01.30.00.600000 AM
1352 01-JAN-22 01.45.00.700000 AM
1353 01-JAN-22 02.00.00.800000 AM
1354 01-JAN-22 02.15.00.900000 AM
1355 01-JAN-22 02.30.01.000000 AM
1356 01-JAN-22 02.45.01.100000 AM
…
…
Attempt #3 failed
TRUNCATE TABLE T3;
/
INSERT into t3 (dt)
with dt (dt, interv) as (
select timestamp '2022-01-01 00:00:00',
numtodsinterval(15,'MINUTE') +
regexp_replace(
numtodsinterval( (rownum * .100000), 'SECOND'), '[^.]+\.(.*)$', '0.\1') from dual
union all
select dt.dt + interv, interv from dt
where dt.dt + interv < date '2022-01-15')
select dt from dt;
/
ORA-30081: invalid data type for datetime/interval arith
Desired output
SEQ_NUM DT
1345 01-JAN-22 12.00.00.000000 AM
1346 01-JAN-22 12.15.00.100000 AM
1347 01-JAN-22 12.30.00.200000 AM
1348 01-JAN-22 12.45.00.300000 AM
1349 01-JAN-22 01.00.00.400000 AM
1350 01-JAN-22 01.15.00.500000 AM
1351 01-JAN-22 01.30.00.600000 AM
1352 01-JAN-22 01.45.00.700000 AM
1353 01-JAN-22 02.00.00.800000 AM
1354 01-JAN-22 02.15.00.900000 AM
1355 01-JAN-22 02.30.00.000000 AM
1356 01-JAN-22 02.45.00.100000 AM
1357 01-JAN-22 03.00.00.200000 AM
…
…

You can use:
INSERT into t3 (dt)
SELECT TIMESTAMP '2022-01-01 00:00:00'
+ (LEVEL - 1) * INTERVAL '15' MINUTE
+ MOD(LEVEL - 1, 10) * INTERVAL '0.1' SECOND
FROM DUAL
CONNECT BY
TIMESTAMP '2022-01-01 00:00:00'
+ (LEVEL - 1) * INTERVAL '15' MINUTE
+ MOD(LEVEL - 1, 10) * INTERVAL '0.1' SECOND < DATE '2022-01-15';
or:
INSERT into t3 (dt)
SELECT TIMESTAMP '2022-01-01 00:00:00'
+ NUMTODSINTERVAL((LEVEL-1)*15*60 + MOD(LEVEL-1, 10)/10, 'SECOND')
FROM DUAL
CONNECT BY
TIMESTAMP '2022-01-01 00:00:00'
+ NUMTODSINTERVAL((LEVEL-1)*15*60 + MOD(LEVEL-1, 10)/10, 'SECOND')
< DATE '2022-01-15';
Which both give the values:
SEQ_NUM
DT
1
2022-01-01 00:00:00.000000
2
2022-01-01 00:15:00.100000
3
2022-01-01 00:30:00.200000
4
2022-01-01 00:45:00.300000
5
2022-01-01 01:00:00.400000
6
2022-01-01 01:15:00.500000
7
2022-01-01 01:30:00.600000
8
2022-01-01 01:45:00.700000
9
2022-01-01 02:00:00.800000
10
2022-01-01 02:15:00.900000
11
2022-01-01 02:30:00.000000
db<>fiddle here

It looks like you just want to add an interval of 15 minutes and 0.1 seconds
select level seq_num,
timestamp '2022-01-01 00:00:00' +
(level-1) * interval '15' minute +
(level-1) * interval '0.1' second dt
from dual
connect by level <= 10
Here's a dbfiddle that shows it producing the output you want.

Related

Calculate only working hours between dates in ORACLE

Hello everyone I have written an oracle query which is calculating working according to 8 hours, but I want according to 8.5 hours result, there is a minor change but I am not getting it please help. Now according to the start and end date, it should return 8.5 working house, but it is returning 8 working hours please assist.
Query
with dates as (
select to_date('20-oct-2022 09:00:00','dd-mon-yyyy hh24:mi:ss') start_dt,
to_date('20-oct-2022 17:30:00','dd-mon-yyyy hh24:mi:ss') end_dt
from dual
),
-- get work hours for each date
t as (
select case level
when 1 then greatest(start_dt,trunc(start_dt) + 8 / 24)
else trunc(start_dt) + level - 16 / 24
end start_dt,
case connect_by_isleaf
when 1 then least(end_dt,trunc(end_dt) + 17 / 24)
else trunc(start_dt) + level - 7 / 24
end end_dt
from dates
connect by level <= trunc(end_dt) - trunc(start_dt) + 1
)
select sum(greatest(end_dt - start_dt,0)) * 24 work_hours
from t
where trunc(start_dt) - trunc(start_dt,'iw') < 5
You do not need to generate all the days; you can directly calculate the number of hours:
SELECT start_dt,
end_dt,
ROUND(
(
-- Calculate the full weeks difference from the start of ISO weeks.
( TRUNC( end_dt, 'IW' ) - TRUNC( start_dt, 'IW' ) ) * 8.5 * (5/7)
-- Add the full days for the final week.
+ LEAST( TRUNC( end_dt ) - TRUNC( end_dt, 'IW' ), 5 ) * 8.5
-- Subtract the full days from the days of the week before the start date.
- LEAST( TRUNC( start_dt ) - TRUNC( start_dt, 'IW' ), 5 ) * 8.5
-- Add the hours of the final day
+ CASE
WHEN end_dt - TRUNC( end_dt, 'IW' ) < 5 -- Weekday
THEN LEAST(
GREATEST(
end_dt - (TRUNC( end_dt ) + INTERVAL '09:00' HOUR TO MINUTE),
0
) * 24,
8.5
)
ELSE 0
END
-- Subtract the hours of the day before the range starts.
- CASE
WHEN start_dt - TRUNC( start_dt, 'IW' ) < 5 -- Weekday
THEN LEAST(
GREATEST(
start_dt - (TRUNC( start_dt ) + INTERVAL '09:00' HOUR TO MINUTE),
0
) * 24,
8.5
)
ELSE 0
END
),
15 -- Number of decimal places
) AS work_hours_diff
FROM dates;
Which, for the sample data:
CREATE TABLE dates (start_dt, end_dt) AS
SELECT DATE '2022-10-20' + INTERVAL '09:00:00' HOUR TO SECOND,
DATE '2022-10-20' + INTERVAL '17:30:00' HOUR TO SECOND
FROM DUAL
UNION ALL
SELECT DATE '2022-10-20' + INTERVAL '10:00:00' HOUR TO SECOND,
DATE '2022-10-21' + INTERVAL '17:30:00' HOUR TO SECOND
FROM DUAL
UNION ALL
SELECT DATE '2022-11-19' + INTERVAL '23:46:00' HOUR TO SECOND,
DATE '2022-11-21' + INTERVAL '12:06:00' HOUR TO SECOND
FROM DUAL
UNION ALL
SELECT DATE '2022-11-18' + INTERVAL '17:30:00' HOUR TO SECOND,
DATE '2022-11-21' + INTERVAL '09:00:00' HOUR TO SECOND
FROM DUAL
UNION ALL
SELECT DATE '2022-11-19' + INTERVAL '12:26:45' HOUR TO SECOND,
DATE '2022-11-21' + INTERVAL '11:02:15' HOUR TO SECOND
FROM DUAL
UNION ALL
SELECT DATE '2022-11-21' + INTERVAL '11:02:15' HOUR TO SECOND,
DATE '2022-11-19' + INTERVAL '12:26:45' HOUR TO SECOND
FROM DUAL;
Outputs:
START_DT
END_DT
WORK_HOURS_DIFF
2022-10-20 09:00:00 (THU)
2022-10-20 17:30:00 (THU)
8.5
2022-10-20 10:00:00 (THU)
2022-10-21 17:30:00 (FRI)
16
2022-11-19 23:46:00 (SAT)
2022-11-21 12:06:00 (MON)
3.1
2022-11-18 17:30:00 (FRI)
2022-11-21 09:00:00 (MON)
0
2022-11-19 12:26:45 (SAT)
2022-11-21 11:02:15 (MON)
2.0375
2022-11-21 11:02:15 (MON)
2022-11-19 12:26:45 (SAT)
-2.0375
Note: the negative value is valid as the start date is after the end date for that row.
fiddle

Query to select rows based on time minus time

I want to build a query on oracle 11g R2 ,I have the following table :
let's take the first row as an example: first row F11 is 1 pm and T11 is 3 pm ,, the difference between 1 pm and 3 pm is 120 minute ,I have a variable X ,let's say X=10, what I want is to create a query to select rows between 1 pm and 3 pm divided by X , so I should have 12 row.
I think it is a hierarchical query you're looking for. Those 12 rows would represent f11 (starting time) plus 120 minutes divided by the x value (which is 10) so - that's 12 minutes.
If that's so, here you go:
SQL> var x number;
SQL> exec :x := 10;
PL/SQL procedure successfully completed.
SQL> alter session set nls_date_format = 'dd.mm.yyyy hh24:Mi:ss';
Session altered.
SQL> with test (id, f11, t11) as
2 (select 1,
3 to_date('28.03.2022 01:00', 'dd.mm.yyyy hh24:mi'),
4 to_date('28.03.2022 03:00', 'dd.mm.yyyy hh24:mi')
5 from dual union all
6 --
7 select 2,
8 to_date('29.11.2021 01:00', 'dd.mm.yyyy hh24:mi'),
9 to_date('29.11.2021 01:30', 'dd.mm.yyyy hh24:mi')
10 from dual
11 ),
12 temp as
13 (select id, f11, (t11 - f11) * (24 * 60) diff
14 from test
15 )
16 select id,
17 f11 + (diff / :x) / (24 * 60) * (column_value - 1) val
18 from temp
19 cross join table(cast(multiset(select level from dual
20 connect by level <= diff / :x
21 ) as sys.odcinumberlist))
22 order by id, val;
ID VAL
---------- -------------------
1 28.03.2022 01:00:00
1 28.03.2022 01:12:00
1 28.03.2022 01:24:00
1 28.03.2022 01:36:00
1 28.03.2022 01:48:00
1 28.03.2022 02:00:00
1 28.03.2022 02:12:00
1 28.03.2022 02:24:00
1 28.03.2022 02:36:00
1 28.03.2022 02:48:00
1 28.03.2022 03:00:00
1 28.03.2022 03:12:00
2 29.11.2021 01:00:00
2 29.11.2021 01:03:00
2 29.11.2021 01:06:00
15 rows selected.
SQL>

Oracle - How to know the load volume by times of a table?

I have some tables in Oracle and I would like to know the variations of the table by sections of time.
I explain, I need a query/script to know how often data is loaded / updated in the table.
example
Can anyone give me ideas on how to do something like that or similar?
Thanks!
Split a day to hours (CTEs times and periods) and apply aggregates to values stored in your_table (which is joined to "fabricated" hours). For example:
SQL> with times as
2 (select trunc(sysdate) + (level - 1)/24 val
3 from dual
4 connect by level <= 25
5 ),
6 periods as
7 (select val val_from,
8 lead(val) over (order by val) val_to
9 from times
10 ),
11 your_table (date_column, ins, upd) as
12 (select trunc(sysdate) + 13/24 + 25/(24*60), 100, 18 from dual union all
13 select trunc(sysdate) + 13/24 + 25/(24*60), 225, null from dual union all
14 select trunc(sysdate) + 14/24 + 33/(24*60), 203, 112 from dual union all
15 select trunc(sysdate) + 15/24 + 15/(24*60), null, 687 from dual union all
16 select trunc(sysdate) + 15/24 + 18/(24*60), null, 987 from dual
17 )
18 select to_char(p.val_from, 'hh24:mi') ||' - '||
19 to_char(p.val_to , 'hh24:mi') period,
20 count(ins) cnt_insert,
21 count(upd) cnt_update
22 from periods p join your_table t on t.date_column >= p.val_from
23 and t.date_column < p.val_to
24 where p.val_to is not null
25 group by p.val_from, p.val_to
26 order by p.val_From;
PERIOD CNT_INSERT CNT_UPDATE
------------- ---------- ----------
13:00 - 14:00 2 1
14:00 - 15:00 1 1
15:00 - 16:00 0 2
SQL>

I have one requirement where I have to show the records between specific date and time every day of one week

I have one requirement where I have to show the records between specific date and time every day in one week duration.
in one week duration( 2019-04-01 till 2019-04-06) ,for instance record of 2019-04-01 at 19 PM till 8 Am of 2019-04-02 ,and record of 2019-04-02 at 19 PM till 08 AM of 2019-04-03 and ...
would you please help me!
Use recursive query to create proper periods then join with your data or do it simpler with condition like here:
select callbegin, callerno
from table4
where callerno in ('7032','750')
and callbegin between timestamp '2019-04-01 19:00:00'
and timestamp '2019-04-06 08:00:00'
and ('19' <= to_char(callbegin, 'hh24') or to_char(callbegin, 'hh24') < '08');
demo
Here's how I understood the question.
SQL> alter session set nls_date_format = 'dd.mm.yyyy hh24:mi';
Session altered.
SQL> break on period;
SQL> with
2 data (id, datum) as
3 (select 1, to_date('01.04.2019 15:30', 'dd.mm.yyyy hh24:mi') from dual union all
4 select 2, to_date('01.04.2019 20:00', 'dd.mm.yyyy hh24:mi') from dual union all -- 1st
5 select 3, to_date('02.04.2019 01:15', 'dd.mm.yyyy hh24:mi') from dual union all -- 1st perios
6 select 4, to_date('02.04.2019 11:00', 'dd.mm.yyyy hh24:mi') from dual union all
7 select 5, to_date('02.04.2019 23:15', 'dd.mm.yyyy hh24:mi') from dual union all -- 2nd period
8 select 6, to_date('03.04.2019 00:10', 'dd.mm.yyyy hh24:mi') from dual union all -- 2nd
9 select 7, to_date('04.04.2019 22:20', 'dd.mm.yyyy hh24:mi') from dual -- 3rd period
10 ),
11 test as
12 (select date '2019-04-01' dstart,
13 date '2019-04-06' dend
14 from dual
15 ),
16 inter as
17 (select dstart + level - 1 datum
18 from test
19 connect by level <= dend - dstart + 1
20 ),
21 from_to as
22 (select datum + 19/24 date_from,
23 lead(datum) over (order by datum) + 8/24 date_to
24 from inter
25 )
26 select f.date_From ||' - '|| f.date_to period,
27 d.id,
28 d.datum
29 from data d join from_to f on 1 = 1
30 where d.datum between f.date_from and f.date_to
31 order by f.date_From, d.id;
PERIOD ID DATUM
----------------------------------- ---------- ----------------
01.04.2019 19:00 - 02.04.2019 08:00 2 01.04.2019 20:00
3 02.04.2019 01:15
02.04.2019 19:00 - 03.04.2019 08:00 5 02.04.2019 23:15
6 03.04.2019 00:10
04.04.2019 19:00 - 05.04.2019 08:00 7 04.04.2019 22:20
SQL>
This is how to filter data by days and time by one week:
With date_list as (
Select
to_date(to_char( (sysdate - level), 'yyyymmdd') || '19', 'yyyymmddhh24') begin_time,
to_date(to_char( ((sysdate - level)+1), 'yyyymmdd') || '08', 'yyyymmddhh24') end_time
From dual connect by level <= 7
)
Select begin_time, your_table.*
From
your_table t1,
date_list t2
Where
t1.your_date between t2.begin_time and t2.end_time;

Time difference in oracle

Hi i have the following table which contains Start time,end time, total time
STARTTIME | ENDTIME | TOTAL TIME TAKEN |
02-12-2013 01:24:00 | 02-12-2013 04:17:00 | 02:53:00 |
I need to update the TOTAL TIME TAKEN field as above using the update query in oracle
For that I have tried the following select query
select round((endtime-starttime) * 60 * 24,2),
endtime,
starttime
from purge_archive_status_log
but I'm getting 02.53 as a result, but my expectation format is 02:53:00 Please let me know how can I do this?
There is probably no reason to have that total_time_taken column in your table at all, you can always calculate it's value. But If you insist on keeping it, it would be better to recreated it as column of interval day to second data type, not varchar2(assuming that that's its current data type). So here are two queries for you to choose from, one returns value of interval day to second data type and another one value of varchar2 data type:
This query returns difference between two dates as a value of interval day to second data type:
SQL> with t1(starttime, endtime, total_time_taken ) as(
2 select to_date('02-12-2013 01:24:00', 'dd/mm/yyyy hh24:mi:ss')
3 , to_date('02-12-2013 04:17:00', 'dd/mm/yyyy hh24:mi:ss')
4 , '02:53:00'
5 from dual
6 )
7 select starttime
8 , endtime
9 , (endtime - starttime) day(0) to second(0) as total_time_taken
10 from t1
11 ;
Result:
STARTTIME ENDTIME TOTAL_TIME_TAKEN
----------- ----------- ----------------
02-12-2013 01:24:00 02-12-2013 04:17:00 +0 02:53:00
This query returns difference between two dates as a value of varchar2 data type:
SQL> with t1(starttime, endtime, total_time_taken ) as(
2 select to_date('02-12-2013 01:24:00', 'dd/mm/yyyy hh24:mi:ss')
3 , to_date('02-12-2013 04:17:00', 'dd/mm/yyyy hh24:mi:ss')
4 , '02:53:00'
5 from dual
6 )
7 select starttime
8 , endtime
9 , to_char(extract(hour from res), 'fm00') || ':' ||
10 to_char(extract(minute from res), 'fm00') || ':' ||
11 to_char(extract(second from res), 'fm00') as total_time_taken
12 from(select starttime
13 , endtime
14 , total_time_taken
15 , (endtime - starttime) day(0) to second(0) as res
16 from t1
17 )
18 ;
Result:
STARTTIME ENDTIME TOTAL_TIME_TAKEN
----------- ----------- ----------------
02-12-2013 01:24:00 02-12-2013 04:17:00 02:53:00
Try this too,
WITH TIME AS (
SELECT to_date('02-12-2013 01:24:00', 'dd-mm-yyyy hh24:mi:ss') starttime,
to_date('02-12-2013 04:17:00', 'dd-mm-yyyy hh24:mi:ss') endTime
FROM dual)
SELECT to_char(TRUNC ((endTime - startTime)* 86400 / (60 * 60)), 'fm09')||':'||
to_char(TRUNC (MOD ((endTime - startTime)* 86400, (60*60)) / 60), 'fm09')||':'||
to_char(MOD((endTime - startTime)* 86400, 60), 'fm09') time_diff
FROM TIME;

Resources