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>
Related
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
I want to create a query that returns number of active products by colander date. I don’t have calendar dim table in database.
Current table
Product_name|prod_id|start_date|end_date
P1|1234|02/01/2020|30/05/2020
P1|2345|02/01/2020|31/12/9999
P1|3456|03/01/2020|31/12/9999
Expected Result
Calander_date|product_name|active_base
01/01/2020|P1|0
02/01/2020|P1|2
03/01/2020|P1|3
01/06/2020|P1|2
Create your own calendar, then - either in the database, as a "real" table (row generator technique helps here), or as a CTE (as I did in the following example):
SQL> with
2 test (product_name, prod_id, start_date, end_date) as
3 -- you have that table; don't type that
4 (select 'P1', 1234, date '2020-01-02', date '2020-05-30' from dual union all
5 select 'P1', 2345, date '2020-01-02', date '9999-12-31' from dual union all
6 select 'P1', 3456, date '2020-01-03', date '9999-12-31' from dual
7 ),
8 calendar (datum) as
9 -- create your own calendar table
10 (select date '2020-01-01' + level - 1
11 from dual
12 connect by level <= 10000 --> number of days you want in calendar
13 )
14 -- final query - join!
15 select c.datum,
16 t.product_name,
17 count(*) active_base
18 from calendar c join test t on c.datum between t.start_date and t.end_date
19 group by c.datum, t.product_name
20 order by c.datum;
DATUM PR ACTIVE_BASE
---------- -- -----------
02/01/2020 P1 2
03/01/2020 P1 3
04/01/2020 P1 3
05/01/2020 P1 3
06/01/2020 P1 3
<snip>
28/05/2020 P1 3
29/05/2020 P1 3
30/05/2020 P1 3
31/05/2020 P1 2
01/06/2020 P1 2
02/06/2020 P1 2
<snip>
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 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;
In WORK_TIME column in my database table (EMP_WORKS), i have records as below.
WORK_TIME
19:03:00
20:00:00
21:02:00
21:54:00
23:04:00
00:02:00
i want to create a database view using these data. for it i need to get Gap between these times as below.
WORK_TIME GAP
19:03:00 -
20:00:00 00:57:00 (Gap between 19:03:00 and 20:00:00)
21:02:00 01:02:00 (Gap between 20:00:00 and 21:02:00)
21:54:00 00:52:00 (Gap between 21:02:00 and 21:54:00)
23:04:00 01:10:00 (Gap between 21:54:00 and 23:04:00)
00:02:00 00:58:00 (Gap between 23:04:00 and 00:02:00)
How could i do this ?
This query will get you the differences in hours:
SELECT
work_time,
( work_time - LAG(work_time) OVER (ORDER BY work_time) ) * 24 AS gap
FROM emp_works
Example on SQL Fiddle returns this:
WORK_TIME GAP
November, 07 2012 19:03:00+0000 (null)
November, 07 2012 20:00:00+0000 0.95
November, 07 2012 21:02:00+0000 1.033333333333
November, 07 2012 21:54:00+0000 0.866666666667
November, 07 2012 23:04:00+0000 1.166666666667
November, 08 2012 00:02:00+0000 0.966666666667
First you will need to have a primary key in the table containing the DATE/TIME field.
I have set up this demo on SQL Fiddle .. Have a look
I have represented the gap as a factor of hours between the two times. You can manipulate the figure to represent minutes, or days, whatever.
SELECT
TO_CHAR(A.WORK_TIME,'HH24:MI:SS') WORK_FROM,
TO_CHAR(B.WORK_TIME,'HH24:MI:SS') WORK_TO,
ROUND(24*(B.WORK_TIME-A.WORK_TIME),2) GAP FROM
sample A,
SAMPLE B
WHERE A.ID+1 = B.ID(+)
If your primary key values have difference greater than 1 (gaps within the values of the primary key) then you will need to offset the value dynamically like this:
SELECT
TO_CHAR(A.WORK_TIME,'HH24:MI:SS') WORK_FROM,
TO_CHAR(B.WORK_TIME,'HH24:MI:SS') WORK_TO,
ROUND(24*(B.WORK_TIME-A.WORK_TIME),2) GAP FROM
sample A,
SAMPLE B
WHERE b.ID = (select min(C.ID) from sample c where c.id>A.ID)
According to your desired result, provided in the question, you want to see time interval. And also I suppose that the WORK_TIME column is of date datatype and there is a date part(otherwise there will be a negative result of subtraction (previous value of WORK_TIME from 00.02.00)).
SQL> create table Work_times(
2 work_time
3 ) as
4 (
5 select to_date('01.01.2012 19:03:00', 'dd.mm.yyyy hh24:mi:ss') from dual union all
6 select to_date('01.01.2012 20:00:00', 'dd.mm.yyyy hh24:mi:ss') from dual union all
7 select to_date('01.01.2012 21:02:00', 'dd.mm.yyyy hh24:mi:ss') from dual union all
8 select to_date('01.01.2012 21:54:00', 'dd.mm.yyyy hh24:mi:ss') from dual union all
9 select to_date('01.01.2012 23:04:00', 'dd.mm.yyyy hh24:mi:ss') from dual union all
10 select to_date('02.01.2012 00:02:00', 'dd.mm.yyyy hh24:mi:ss') from dual
11 )
12 /
Table created
SQL>
SQL> select to_char(t.work_time, 'hh24.mi.ss') work_time
2 , (t.work_time -
3 lag(t.work_time) over(order by WORK_TIME)) day(1) to second(0) Res
4 from work_times t
5 ;
WORK_TIME RES
--------- -------------------------------------------------------------------------------
19.03.00
20.00.00 +0 00:57:00
21.02.00 +0 01:02:00
21.54.00 +0 00:52:00
23.04.00 +0 01:10:00
00.02.00 +0 00:58:00
6 rows selected