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

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>

Related

How to create a statement for subtract a date omitting weekends and holidays in Oracle PLSQL

I need to make a query where from a final date specific days are subtracted, omitting weekends (Saturday and Sunday) and holidays,
Once the operation is done, it proceeds to display the date resulting from that operation, for example:
(3/1/2023 - 7) = 2/20/2023
On March 3, 7 days are subtracted, if I apply that statement it should show 2/23/2023 but since within that operation it went through Saturday and Sunday, it must omit them and continue subtracting as long as the days are between
Monday to Friday.
I've a table where I already have the festive dates, I would only check if there is also a festive date, it is omitted and continues subtracting.
Is possible to create that statement?
Here's one option; it uses holidays table (which you have) and a function (which creates a calendar, marks weekends and holidays and skips them in a loop).
Holidays:
SQL> select * from holidays order by datum;
DATUM
----------
01.11.2022
25.12.2022
26.12.2022
01.01.2023
06.01.2023
Function:
SQL> create or replace function f_result
2 (par_datum in date, par_number_of_days in number)
3 return date
4 is
5 retval date := par_datum;
6 i number := 0;
7 begin
8 for cur_r in
9 -- calendar
10 (with
11 temp (datum) as
12 (select par_datum - level + 1
13 from dual
14 connect by level <= par_number_of_days + 20
15 )
16 -- mark weekends and holidays
17 select t.datum,
18 case when to_char(t.datum, 'dy') in ('sat', 'sun') then 1 else 0 end cb_weekend,
19 case when t.datum = h.datum then 1 else 0 end cb_holiday
20 from temp t left join holidays h on h.datum = t.datum
21 order by t.datum desc
22 ) loop
23 retval := cur_r.datum;
24 -- skip weekends and holidays
25 i := i + case when cur_r.cb_weekend = 1 or cur_r.cb_holiday = 1 then 0 else 1 end;
26 exit when i > par_number_of_days;
27 end loop;
28 return retval;
29 end;
30 /
Function created.
Calendar (January 2023; names are in Croatian, but disregard that. Weekends and holidays are marked in different color. Today's date is 20.01.2023 (dd.mm.yyyy)):
Testing:
SQL> select f_result(date '2023-01-20', 10) from dual;
F_RESULT(D
----------
05.01.2023
SQL> select f_result(date '2023-01-10', 7) from dual;
F_RESULT(D
----------
29.12.2022
SQL>
If you'd want to move forward, slightly change function code:
SQL> create or replace function f_result
2 (par_datum in date, par_number_of_days in number)
3 return date
4 is
5 retval date := par_datum;
6 i number := 0;
7 begin
8 for cur_r in
9 -- calendar
10 (with
11 temp (datum) as
12 (select par_datum + level - 1
13 from dual
14 connect by level <= par_number_of_days + 20
15 )
16 -- mark weekends and holidays
17 select t.datum,
18 case when to_char(t.datum, 'dy') in ('sat', 'sun') then 1 else 0 end cb_weekend,
19 case when t.datum = h.datum then 1 else 0 end cb_holiday
20 from temp t left join holidays h on h.datum = t.datum
21 order by t.datum
22 ) loop
23 retval := cur_r.datum;
24 -- skip weekends and holidays
25 i := i + case when cur_r.cb_weekend = 1 or cur_r.cb_holiday = 1 then 0 else 1 end;
26 exit when i > par_number_of_days;
27 end loop;
28 return retval;
29 end;
30 /
Function created.
For example:
SQL> select f_result(date '2022-12-30', 1) res_1,
2 f_result(date '2023-01-04', 7) res_2,
3 f_result(date '2023-03-03', 7) res_3,
4 f_result(date '2023-03-01', 7) res_4
5 from dual;
RES_1 RES_2 RES_3 RES_4
---------- ---------- ---------- ----------
02.01.2023 16.01.2023 14.03.2023 10.03.2023
SQL>
Specifically, RES_3 you mentioned in comment (as 3rd January 2023 + 7 days) is:
03.01.2023 + 7 =
day 1: 04.01.
day 2: 05.01.
skip 06.01. (holiday), 07.01. and 08.01. (weekend)
day 3: 09.01.
day 4: 10.01.
day 5: 11.01.
day 6: 12.01.
day 7: 13.01. --> RES_3
declare
v_result_date date := to_date('03/01/2023','mm/dd/yyyy');
v_days number := 7;
begin
while v_days > 0
loop
if to_char(v_result_date,'dy') not in ('sat','sun')
then
v_days := v_days - 1;
end if;
v_result_date := v_result_date - 1;
end loop;
dbms_output.put_line(v_result_date);
end;
This could be done with SQL using model clause. You could try this.
SAMPLE data for holydays and your DATE 2023-03-01 with 7 days backward count taking care of weekends and holydays:
WITH
holydays (A_DATE) AS
(
Select DATE '2023-01-01' From Dual Union All
Select DATE '2023-01-06' From Dual Union All
Select DATE '2023-04-09' From Dual Union All
Select DATE '2023-04-10' From Dual Union All
Select DATE '2023-06-08' From Dual Union All
Select DATE '2023-06-22' From Dual Union All
Select DATE '2023-08-05' From Dual Union All
Select DATE '2023-08-15' From Dual Union All
Select DATE '2023-11-01' From Dual Union All
Select DATE '2023-11-18' From Dual Union All
Select DATE '2023-12-25' From Dual Union All
Select DATE '2023-12-26' From Dual
),
test_period AS
(
Select To_Date('2023-03-01', 'yyyy-mm-dd') "REF_DATE", 7 "NUM_OF_DAYS" From Dual
),
Now, using Oracle SQL MODEL Clause, we could createCTE returning a dataset that will have a kind of reversed set of dates excluding weekend and holyday days.
days AS
( SELECT IDX, REF_DATE, ROWNUM - 1 "RN", DAY_OF_WEEK, NUM_OF_DAYS
FROM ( Select IDX, REF_DATE, NUM_OF_DAYS,
CASE WHEN Nvl(To_Char(A_DATE), 'N') = 'N' And
Case When SubStr(To_Char(REF_DATE, 'Day'), 1, 3) IN('Sat', 'Sun') Then 'Y' Else 'N' End = 'N'
THEN 1
END "DAYS_PAST",
To_Char(REF_DATE, 'Day') "DAY_OF_WEEK",
A_DATE,
Nvl(To_Char(A_DATE), 'N') "IS_HOLYDAY",
Case When SubStr(To_Char(REF_DATE, 'Day'), 1, 3) IN('Sat', 'Sun') Then 'Y' Else 'N' End "IS_WEEKEND"
From ( Select REF_DATE, NUM_OF_DAYS From test_period ) p
Left Join holydays h ON(h.A_DATE = REF_DATE)
MODEL
Dimension By (0 as IDX)
Measures (REF_DATE, NUM_OF_DAYS, A_DATE, 0 AS DAYS_PAST)
RULES ITERATE(100) -- NUMBER OF ITERATIONS SHOULD BE BIGGER TO COVER SKIPPED DAYS
(
REF_DATE[ITERATION_NUMBER] = REF_DATE[0] - ITERATION_NUMBER,
NUM_OF_DAYS[ITERATION_NUMBER] = NUM_OF_DAYS[0]
)
Order by REF_DATE DESC
)
WHERE DAYS_PAST Is Not Null And ROWNUM <= NUM_OF_DAYS + 1
)
R e s u l t :
IDX REF_DATE RN DAY_OF_WEEK NUM_OF_DAYS
---------- --------- ---------- ----------- -----------
0 01-MAR-23 0 Wednesday 7
1 28-FEB-23 1 Tuesday 7
2 27-FEB-23 2 Monday 7
5 24-FEB-23 3 Friday 7
6 23-FEB-23 4 Thursday 7
7 22-FEB-23 5 Wednesday 7
8 21-FEB-23 6 Tuesday 7
9 20-FEB-23 7 Monday 7
Now just select the date you want - the 7th date backwards counting just working days.
SELECT REF_DATE "TARGET_DATE"
FROM days
WHERE RN = NUM_OF_DAYS
7 days from 2023-03-01
TARGET_DATE
-----------
20-FEB-23

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>

I need to add a column that assigns row numbers to each record

I am trying to add a column that assigns row numbers to each record
I made changes to the code to add the desired column
But I was getting duplicated row numbers. How do I get unique row numbers?
.
**Note: This code can be executed in the SQL editor like that. It needs no sample data**
select distinct trunc(GenerateTimeBy1Day,'day') as claim_eff_date, trunc(GenerateTimeBy1DayPlus20,'day') as bwe_to_completeby from
(
select from_dt + (level - 1)/1 as GenerateTimeBy1Day, (from_dt + (level - 1)/1) + 20 as GenerateTimeBy1DayPlus20
from (select from_dt
,to_dt
,to_dt - from_dt + 1 as days_between
from (select to_date('03-Jan-2021') as from_dt
, to_date('30-Jan-2021') as to_dt
from dual))
connect by (level - 1) <= days_between
)
order by claim_eff_date
I made these changes to the code to add the desired column
**Note: This code can be executed in the SQL editor like that. It needs no sample data**
select distinct trunc(GenerateTimeBy1Day,'day') as claim_eff_date, trunc(GenerateTimeBy1DayPlus20,'day') as bwe_to_completeby, row_number()
over (PARTITION BY trunc(GenerateTimeBy1Day,'day'), trunc(GenerateTimeBy1DayPlus20,'day') ORDER BY trunc(GenerateTimeBy1Day,'day')) as row_number from
(
select from_dt + (level - 1)/1 as GenerateTimeBy1Day, (from_dt + (level - 1)/1) + 20 as GenerateTimeBy1DayPlus20
from (select from_dt
,to_dt
,to_dt - from_dt + 1 as days_between
from (select to_date('03-Jan-2021') as from_dt
, to_date('30-Jan-2021') as to_dt
from dual))
connect by (level - 1) <= days_between
)
order by claim_eff_date
But I am getting
Row_Number
----------
1
1
2
3
4
5
6
1
1
2
3
How do I get unique row numbers?
Row_Number
----------
1
2
3
4
5
6
7
8
9
10
I'm not sure whet those dates represent, but - if query you initially wrote does the job, then use it as source for the final query which calculates row number:
SQL> alter session set nls_date_format = 'dd.mm.yyyy';
Session altered.
SQL> with your_query as (
2 select distinct trunc(GenerateTimeBy1Day,'day') as claim_eff_date,
3 trunc(GenerateTimeBy1DayPlus20,'day') as bwe_to_completeby
4 from
5 (
6 select from_dt + (level - 1)/1 as GenerateTimeBy1Day, (from_dt + (level - 1)/1) + 20 as GenerateTimeBy1DayPlus20
7 from (select from_dt
8 ,to_dt
9 ,to_dt - from_dt + 1 as days_between
10 from (select date '2021-01-03' as from_dt
11 , date '2021-01-30' as to_dt
12 from dual))
13 connect by (level - 1) <= days_between
14 ))
15 select claim_eff_date,
16 bwe_to_completeby,
17 row_number() over (order by claim_eff_date) rn
18 from your_query
19 order by claim_eff_date;
CLAIM_EFF_ BWE_TO_COM RN
---------- ---------- ----------
28.12.2020 18.01.2021 1
04.01.2021 25.01.2021 2
04.01.2021 18.01.2021 3
11.01.2021 01.02.2021 4
11.01.2021 25.01.2021 5
18.01.2021 08.02.2021 6
18.01.2021 01.02.2021 7
25.01.2021 08.02.2021 8
25.01.2021 15.02.2021 9
9 rows selected.
SQL>
By the way, this is what you wrote: to_date('03-Jan-2021'). That's wrong. TO_DATE applied to a string without date format mask relies on Oracle's capabilities to guess what you meant to say. Besides, that won't even work in my database, although you said
This code can be executed in the SQL editor
My database speaks Croatian and there's no month like Jan.
Safer option is to
apply format mask: to_date('03-Jan-2021', 'dd-mon-yyyy', 'nls_date_language = english')
or, use date literal (like I did): date '2021-01-03' which ALWAYS has date keyword and date in yyyy-mm-dd format, so there's no ambiguity

How to select the first 5 dates from each group and put them in a single column separated by comma in Oracle?

I have a table like this:
Division
Region
Date of Last Visit
1
2
11/20/2021
1
2
11/18/2021
1
7
10/18/2021
1
7
11/19/2021
2
2
11/17/2021
2
3
09/20/2021
2
3
10/20/2021
I want to write a query that groups by the division and region columns and gives me the last 5 dates for each group separated by commas in a single column. Something like this:
Division
Region
Date of Last Visit
Today
Days since last visit
1
2
11/20/2021, 11/18/2021
sysdate
sysdate - max(date of last visit)
1
7
10/18/2021, 11/19/2021
sysdate
sysdate - max(date of last visit)
2
2
11/17/2021
sysdate
sysdate - max(date of last visit)
2
3
9/20/2021, 10/20/2021
sysdate
sysdate - max(date of last visit)
The last two columns are custom calculated columns that I also need for the final output table. Any help would be greatly appreciated as I have tried a lot of things but I keep getting errors about it not being grouped properly, possibly because of the two extra columns at the end. But even without that, I am not sure how to fetch only the last 5 dates per group in oracle.
Thanks!
You want to filter the greatest-n-per-group using the ROW_NUMBER analytic function and then aggregate:
SELECT division,
region,
LISTAGG(TO_CHAR(date_of_last_visit, 'DD/MM/YYYY'), ',')
WITHIN GROUP (ORDER BY date_of_last_visit DESC)
AS date_of_last_visit,
SYSDATE AS today,
TRUNC(SYSDATE - MAX(date_of_last_visit)) AS days_since_last_visit
FROM (
SELECT t.*,
ROW_NUMBER() OVER (PARTITION BY division, region
ORDER BY date_of_last_visit DESC) AS rn
FROM table_name t
)
WHERE rn <= 5
GROUP BY
division,
region
Which, for the sample data:
CREATE TABLE table_name (division, region, date_of_last_visit) as
select 1, 2, date '2021-11-20' from dual union all
select 1, 2, date '2021-11-18' from dual union all
select 1, 7, date '2021-10-18' from dual union all
select 1, 7, date '2021-11-19' from dual union all
select 2, 2, date '2021-11-17' from dual union all
select 2, 3, date '2021-09-20' from dual union all
select 2, 3, date '2021-10-20' from dual;
Outputs:
DIVISION
REGION
DATE_OF_LAST_VISIT
TODAY
DAYS_SINCE_LAST_VISIT
1
2
20/11/2021,18/11/2021
30-NOV-21
10
1
7
19/11/2021,18/10/2021
30-NOV-21
11
2
2
17/11/2021
30-NOV-21
13
2
3
20/10/2021,20/09/2021
30-NOV-21
41
db<>fiddle here
Here you go; read comments within code.
SQL> with test (division, region, datum) as
2 -- sample data
3 (select 1, 2, date '2021-11-20' from dual union all
4 select 1, 2, date '2021-11-18' from dual union all
5 select 1, 7, date '2021-10-18' from dual union all
6 select 1, 7, date '2021-11-19' from dual union all
7 select 2, 2, date '2021-11-17' from dual union all
8 select 2, 3, date '2021-09-20' from dual union all
9 select 2, 3, date '2021-10-20' from dual
10 ),
11 temp as
12 -- rank rows per division/region, sorted by date column in descending order
13 (select t.*,
14 rank() over (partition by division, region order by datum desc) rnk
15 from test t
16 )
17 -- select up to 5 last rows per division/region
18 select division, region,
19 listagg(datum, ', ') within group (order by datum) dates,
20 trunc(sysdate) today,
21 --
22 (select trunc(sysdate) - a.datum
23 from temp a
24 where a.division = t.division
25 and a.region = t.region
26 and a.rnk = 1) days_since
27 from temp t
28 where rnk <= 5
29 group by division, region
30 order by division, region;
DIVISION REGION DATES TODAY DAYS_SINCE
---------- ---------- ------------------------------ ---------- ----------
1 2 11/18/2021, 11/20/2021 11/30/2021 10
1 7 10/18/2021, 11/19/2021 11/30/2021 11
2 2 11/17/2021 11/30/2021 13
2 3 09/20/2021, 10/20/2021 11/30/2021 41
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;

Resources