I have the below table. I need to create a row for each month from hire_dt to term_dt
id hire_dt term_dt
1 08/07/2017 02/20/2018
Expected Results:
id hire_dt term_dt Month level_alias
1 08/07/2017 10/20/2017 201708 1
1 08/07/2017 10/20/2017 201709 2
1 08/07/2017 10/20/2017 201710 3
Query:
SELECT DISTINCT
ID,
HIRE_DT,
TERM_DT,
TO_CHAR(ADD_MONTHS(TRUNC(HIRE_DT, 'MM'), LEVEL -1), 'YYYYMM') AS MONTH,
LEVEL AS LEVEL_ALIAS
FROM AHR
JOIN HA ON ID = HA.id --AND RNK = 1
WHERE 1=1
CONNECT BY LEVEL <= 1 + MONTHS_BETWEEN(TRUNC(TERM_DT,'MM'),TRUNC(HIRE_DT,'MM'))
AND PRIOR ID = ID AND PRIOR HIRE_DT=HIRE_DT
AND PRIOR SYS_GUID() IS NOT NULL
AND AHR.ASSOC_AIN_ID = 1
Results:
id hire_dt term_dt Month level_alias
1 08/07/2017 201708 1
1 08/07/2017 10/20/2017 201708 1
1 08/07/2017 10/20/2017 201709 2
1 08/07/2017 10/20/2017 201710 3
Why am I receiving the null term_dt?
Based on (somewhat misleading; 10/20/2017 vs. 02/20/2018) sample data you provided, have a look at this:
SQL> with test (id, hire_dt, term_dt) as
2 (select 1, date '2017-08-07', date '2017-10-20' from dual)
3 select id,
4 hire_dt,
5 term_dt,
6 to_char(add_months(hire_dt, level - 1), 'yyyymm') month,
7 level lvl
8 from test
9 connect by level <= months_between(term_dt, hire_dt) + 1;
ID HIRE_DT TERM_DT MONTH LVL
---------- ---------- ---------- ------ ----------
1 08/07/2017 10/20/2017 201708 1
1 08/07/2017 10/20/2017 201709 2
1 08/07/2017 10/20/2017 201710 3
SQL>
It might need to be modified, but - as I said, with information you provided, that's it (i.e. I have no idea what tables you're joining and why).
Related
Could you please assist me to split each row and create multiple rows for each date between begin_date and end_date.
ID CODE VIEW BEGIN_DATE END_DATE
-------------------------------------------
10400 null 2 17-FEB-20 17-FEB-20
10650 null 2 17-FEB-20 18-FEB-20
10900 null 2 19-FEB-20 21-FEB-20
10901 null 2 21-FEB-20 02-MAR-20
11650 2723 2 02-MAR-20 04-MAR-20
11650 1002 2 02-MAR-20 04-MAR-20
11650 1001 2 02-MAR-20 04-MAR-20
11650 1000 2 02-MAR-20 04-MAR-20
Currently I'm using below query but it doesn't seem to work
select
r.*
from rec r
connect by level <= end_date - begin_date + 1;
what i want is to some thing like this
ID CODE VIEW DATE
----------------------------------
11650 2723 2 02-MAR-20
11650 2723 2 03-MAR-20
11650 2723 2 04-MAR-20
.... continue
Here's one option:
Sample data:
SQL> with test (id, code, cview, begin_date, end_date) as
2 (select 10400, null, 2, date '2020-02-17', date '2020-02-17' from dual union all
3 select 10650, null, 2, date '2020-02-17', date '2020-02-18' from dual union all
4 select 11650, 2723, 2, date '2020-03-02', date '2020-03-04' from dual
5 )
Query begins here:
6 select id,
7 code,
8 cview,
9 begin_date + column_value - 1 as datum
10 from test cross join
11 table(cast(multiset(select level from dual
12 connect by level <= end_date - begin_date + 1
13 ) as sys.odcinumberlist))
14 order by id, datum;
ID CODE CVIEW DATUM
---------- ---------- ---------- ----------
10400 2 17.02.2020
10650 2 17.02.2020
10650 2 18.02.2020
11650 2723 2 02.03.2020
11650 2723 2 03.03.2020
11650 2723 2 04.03.2020
6 rows selected.
SQL>
I have a select which shows me how much lasts a process, it looks like:
SELECT
info1,
info2,
ifno3...
(DATE,'DD.MM.YYYY')DAY,
to_char(DATE,'HH24:MI') hour_from,
to_char(DATE_UNTIL,''HH24:MI'')hour_until,
REGEXP_SUBSTR((date_until-date)DAY TO SECOND,'d{2}:\d{2}')lasts_time
.....
and if a process lasts for example from 09.06.2021 23:00 to 10.06.2021 07:00, then the output of query from above will be:
info1 info2 info3 DAY hour_from hour_until lasts_time
info1| info2 |info3 |09.06.2021 |23:00 | 07:00 | 08:00
and i want it to change in
if this process lasts over night, then the output should be something like this:
info1 info2 info3 DAY hour_from hour_until lasts_time
info1| info2 |info3 |09.06.2021 |23:00 | 00:00 | 01:00
info1| info2 |info3 |10.06.2021 |00:00 | 07:00 | 07:00
so it will count only until the day ends, and if the process is still on going in the new day the output will come in a new row,
I think I have here to deal with union all, but I'm not sure, if anyone has any idea would be great
Here's one option. Read comments within code.
SQL> with
2 test (id, info1, date_from, date_to) as
3 -- sample data
4 (select 1, 'info1', to_date('09.06.2021 23:00', 'dd.mm.yyyy hh24:mi'),
5 to_date('10.06.2021 07:00', 'dd.mm.yyyy hh24:mi')
6 from dual
7 ),
8 temp as
9 -- row generator, to generate all hours between DATE_FROM and DATE_TO
10 (select id, info1,
11 date_from + (level - 1) / 24 datum,
12 lead(date_from + (level - 1) / 24) over (order by date_from) next_datum
13 from test
14 connect by level <= (date_to - date_from) * 24 + 1
15 ),
16 temp2 as
17 -- find date boundaries (MINDAT and MAXDAT), as well as duration in between (LASTS_TIME)
18 (select id, info1, datum, next_datum,
19 min(datum) mindat,
20 max(case when to_char(next_datum, 'mi') = '00' then next_datum - 1/(24*60*60)
21 else datum
22 end
23 ) maxdat,
24 --
25 round((max(case when to_char(next_datum, 'mi') = '00' then next_datum - 1/(24*60*60)
26 else datum
27 end
28 ) -
29 min(datum)
30 ) * 24) lasts_time
31 from temp
32 group by id, info1, datum, next_datum
33 )
34 -- the final result
35 select id,
36 info1,
37 to_char(datum, 'dd.mm.yyyy') day,
38 min(to_char(datum, 'hh24:mi')) hour_from,
39 max(to_char(next_datum, 'hh24:mi')) hour_until,
40 sum(lasts_time) lasts_time
41 from temp2
42 group by id, info1,
43 to_char(datum, 'dd.mm.yyyy')
44 order by day;
The result is
ID INFO1 DAY HOUR_FROM HOUR_UNTIL LASTS_TIME
---------- ----- ---------- ---------- ---------- ----------
1 info1 09.06.2021 23:00 00:00 1
1 info1 10.06.2021 00:00 07:00 7
SQL>
For #astentx's comment: there's no endless loop in my case for values you suggested:
SQL> set timing on
SQL> with
2 test (id, info1, date_from, date_to) as
3 -- sample data
4 (select 1, 'info1', to_date('10.06.2021 07:00', 'dd.mm.yyyy hh24:mi'),
5 to_date('11.06.2021 07:00', 'dd.mm.yyyy hh24:mi')
6 from dual
7 ),
<snip>
ID INFO1 DAY HOUR_ HOUR_ LASTS_TIME
---------- ----- ---------- ----- ----- ----------
1 info1 10.06.2021 07:00 23:00 17
1 info1 11.06.2021 00:00 07:00 7
Elapsed: 00:00:00.04
SQL>
You can use recursive CTE to generate required amount of rows (which are equal to days distance between dates).
with a as (
select
'info1' as id
, to_date('2021-05-01 10:20:30') as dt_from
, to_date('2021-05-03 07:08:09') as dt_to
from dual
union all
select
'info2'
, to_date('2021-05-02 05:06:07')
, to_date('2021-05-02 10:20:30')
from dual
)
, b(id, dt_from, dt_to, dt) as (
select
id
, dt_from
, dt_to
, trunc(dt_from)
from a
union all
select
id
, dt_from
, dt_to
, dt + 1
from b
where dt < trunc(dt_to)
)
select
id
, dt
, numtodsinterval(
case
when trunc(dt_to) = dt
then dt_to
else dt + 1
end
-
greatest(dt_from, dt)
, 'DAY') as dur_interval
/*Or any rounding function you need*/
, trunc((case
when trunc(dt_to) = dt
then dt_to
else dt + 1
end
-
greatest(dt_from, dt))*24) as dur_hours
, case
when trunc(dt_from) = dt
then to_char(dt_from, 'hh24:mi')
else '00:00'
end as hour_from
, case
when trunc(dt_to) = dt
then to_char(dt_to, 'hh24:mi')
else '23:59'
end as hour_to
from b
order by
id
, dt
ID | DT | DUR_INTERVAL | DUR_HOURS | HOUR_FROM | HOUR_TO
:---- | :------------------ | :---------------------------- | --------: | :-------- | :------
info1 | 2021-05-01 00:00:00 | +000000000 13:39:30.000000000 | 13 | 10:20 | 23:59
info1 | 2021-05-02 00:00:00 | +000000001 00:00:00.000000000 | 24 | 00:00 | 23:59
info1 | 2021-05-03 00:00:00 | +000000000 07:08:09.000000000 | 7 | 00:00 | 07:08
info2 | 2021-05-02 00:00:00 | +000000000 05:14:23.000000000 | 5 | 05:06 | 10:20
db<>fiddle here
I have a sample table below which shows the ticket number, time when the ticket was opened and time when it was closed.
TKTNUM OPEN_DATE CLOSE_DATE
1234 12-Mar-19 08:36 14-Mar-19 08:36
1235 13-Mar-19 08:36 15-Mar-19 08:36
1236 14-Mar-19 08:36 16-Mar-19 08:36
1237 15-Mar-19 08:36
1238 16-Mar-19 08:36
1239 17-Mar-19 08:36
1240 18-Mar-19 08:36 20-Mar-19 08:36
1241 19-Mar-19 08:36 20-Mar-19 08:36
1242 20-Mar-19 08:36 21-Mar-19 08:36
I need to count the number of open/closed tickets on a given day...
DATE OPEN CLOSED
12-Mar-19 08:36 1 0
13-Mar-19 08:36 2 0
14-Mar-19 08:36 2 1
15-Mar-19 08:36 2 2
16-Mar-19 08:36 2 3
17-Mar-19 08:36 3 3
18-Mar-19 08:36 4 3
19-Mar-19 08:36 5 3
20-Mar-19 08:36 4 5
Any help is greatly appreciated. Thanks
Used the query(c/o Tejash) below on a sample job_history table
EMPLOYEE_ID START_DATE END_DATE JOB_ID DEPARTMENT_ID
----------- -------------------- -------------------- ---------- -------------
200 17/SEP/1995 00:00:00 17/JUN/2001 00:00:00 AD_ASST 90
101 21/SEP/1997 00:00:00 27/OCT/2001 00:00:00 AC_ACCOUNT 110
102 13/JAN/2001 00:00:00 24/JUL/2006 00:00:00 IT_PROG 60
101 28/OCT/2001 00:00:00 15/MAR/2005 00:00:00 AC_MGR 110
200 01/JUL/2002 00:00:00 31/DEC/2006 00:00:00 AC_ACCOUNT 90
201 17/FEB/2004 00:00:00 19/DEC/2007 00:00:00 MK_REP 20
114 24/MAR/2006 00:00:00 31/DEC/2007 00:00:00 ST_CLERK 50
176 24/MAR/2006 00:00:00 31/DEC/2006 00:00:00 SA_REP 80
176 01/JAN/2007 00:00:00 31/DEC/2007 00:00:00 SA_MAN 80
122 01/JAN/2007 00:00:00 31/DEC/2007 00:00:00 ST_CLERK 50
With dates(dt)
As (Select mindt + level - 1 from
(Select min(start_date) mindt, max(end_date) maxdt from job_history)
Connect by level <= maxdt - mindt + 1)
Select dt,
sum(case when dt between start_date and coalesce(end_date,dt) then 1 end) as startdate,
Sum(case when dt >= end_date then 1 end) as enddate
From dates cross join job_history
Group by dt
Order by dt desc
On 17/JUN/2001, the query gave
DT STARTDATE ENDDATE
-------------------- ---------- ----------
31/DEC/2007 00:00:00 3 10
<SNIPPED>
17/JUN/2001 00:00:00 3 1
Instead of
DT STARTDATE ENDDATE
-------------------- ---------- ----------
31/DEC/2007 00:00:00 3 10
<SNIPPED>
17/JUN/2001 00:00:00 2 1
Tried to edit the query and now its giving me
DT STARTDATE ENDDATE
-------------------- ---------- ----------
31/DEC/2007 00:00:00 <<< 10
<snipped>
18/JUN/2001 00:00:00 2 1
17/JUN/2001 00:00:00 2 <<< 1
16/JUN/2001 00:00:00 3 1
You can use dates as cte for total days and join it again with same table as following:
With dates(dt)
As
(
Select mindt + level - 1 from
(Select min(open_date) mindt, max(open_dt) maxdt from your_table)
Connect by level <= maxdt - mindt + 1
)
Select dt,
sum(case when dt between open_date and coalesce(close_date,dt) then 1 end) as open,
Sum(case when dt >= close_date then 1 end) as closed
From dates cross join your_table
Group by dt;
Cheers!!
You can unpivot and aggregate:
select dte, sum(is_open) as num_opens, sum(is_close) as num_closes
from ((select open_date as dte, 1 as is_open, 0 as is_close
from t
) union all
(select close_date, 0 as is_open, 1 as is_close
from t
)
) t
group by dte
order by dte;
Note: It is probably a good idea to truncate the date so it has no time component:
select trunc(dte), sum(is_open) as num_opens, sum(is_close) as num_closes
from ((select open_date as dte, 1 as is_open, 0 as is_close
from t
) union all
(select close_date, 0 as is_open, 1 as is_close
from t
)
) t
where dte is not null
group by trunc(dte)
order by trunc(dte);
And in Oracle 12C you can use a lateral join for this:
select trunc(dte), sum(is_open), sum(is_close)
from t cross join lateral
(select t.open_date as dte, 1 as is_open, 0 as is_close from dual union all
select t.close_date, 0 as is_open, 1 as is_close from dual
) t
group by trunc(dte)
order by trunc(dte);
I need some help in finding the number of reliefs each teacher has, every single day, 2 months before the teacher resigns.
Join_dt - teacher's join date,
Resign_dt - teacher's resign date,
Relief_ID - Relief teacher's ID,
Start_dt - Relief's start date,
End_dt - Relief's end date,
note that there may be overlapping dates between 2 or more different reliefs and so I need to find the number of distinct reliefs each teacher has for each date.
This is what I am given:
Teacher_ID Join_dt Resign_dt Relief_ID Start_dt End_dt
12 2006-08-30 2019-08-01 20 2017-02-07 2019-07-04
12 2006-08-30 2019-08-01 20 2016-11-10 2019-01-30
12 2006-08-30 2019-08-01 103 2016-08-20 2019-07-29
12 2006-08-30 2019-08-01 17 2016-01-30 2017-12-30
23 2017-10-01 2018-11-12 44 2018-10-19 2018-11-11
23 2017-10-01 2018-11-12 29 2018-04-01 2018-12-02
23 2017-10-01 2018-11-12 06 2017-11-25 2018-05-02
05 2015-02-11 2019-10-02 38 2019-01-17 2019-07-21
05 2015-02-11 2019-10-02 11 2018-11-02 2019-02-05
05 2015-02-11 2019-10-02 15 2018-09-30 2018-10-03
Expected result:
Teacher_ID Dates No_of_reliefs
12 2019-07-31 0
12 2019-07-30 0
12 2019-07-29 1
12 2019-07-28 1
12 2019-07-27 1
... ...
12 2019-07-04 2
... ...
12 2016-05-30 2
12 2016-05-29 2
12 2016-05-28 2
12 2016-05-27 2
12 2016-05-26 1
23 2018-10-31 2
... ...
For date 2019-07-29, No_of_reliefs = 1 because of Relief_ID 103.
For date 2017-07-04, No_of_reliefs = 2 because of Relief_ID 20 & 103.
Dates are supposed to start from 1 month before the teacher resigned. For Teacher_ID 23, since she resigned on 2019-11-12, dates shall start from 2019-10-31.
I have tried using connect by but the execution time is really long since it involves a large amount of data.
Any other methods will be greatly appreciated!!
Thank you kind souls!!!
You can use
connect by level <= last_day(add_months(Resign_dt,-1)) - add_months(Resign_dt,-2) clause :
I suppose you mean 2 months before resignment for the starting date, and ending on the last day of the previous month.
with t1(Teacher_ID,Resign_dt,Relief_ID,start_dt,end_dt) as
(
select 12,date'2019-08-01',20 ,date'2017-02-07',date'2019-07-04' from dual union all
select 12,date'2019-08-01',20 ,date'2016-11-10',date'2019-01-30' from dual union all
select 12,date'2019-08-01',103,date'2016-08-20',date'2019-07-29' from dual
......
), t2 as
(
select distinct last_day(add_months(Resign_dt,-1)) - level + 1 as Resign_dt, Teacher_ID
from t1
connect by level <= last_day(add_months(Resign_dt,-1)) - add_months(Resign_dt,-2)
and prior Teacher_ID = Teacher_ID and prior sys_guid() is not null
)
select Teacher_ID, to_char(Resign_dt,'yyyy-mm-dd') as Dates,
(select count(distinct Relief_ID)
from t1
where t2.Resign_dt between start_dt and end_dt
and t2.Teacher_ID = Teacher_ID
)
from t2
order by Teacher_ID, Resign_dt desc;
Demo
select d.dt
, tr.Teacher_ID
--, tr.Join_dt
--, tr.Resign_dt
, count(tr.Relief_ID)
--, tr.Start_dt
--, tr.End_dt
from tr
right outer join (
SELECT dt
FROM (
SELECT DATE '2006-01-01' + ROWNUM - 1 dt
FROM DUAL CONNECT BY ROWNUM < 5000
) q
WHERE EXTRACT(YEAR FROM dt) < EXTRACT(YEAR FROM sysdate) + 2
--order by 1
) d on d.dt between tr.Join_dt and tr.End_dt
and d.dt between tr.Start_dt and tr.Resign_dt
group by d.dt
, tr.Teacher_ID
order by d.dt desc
I have an Oracle 11g and I want to know if it is possible to make a select from a date to another date.
For example:
I have two fields called StartDate and EndDate. I want to show the count of rows between EndDate and StartDate.
If my StartDate is 2018-08-01 and my EndDate is 2018-08-10 so my expected table should be:
DATE | rownum
2018-08-01 | 1
2018-08-02 | 2
2018-08-03 | 3
2018-08-04 | 4
2018-08-05 | 5
2018-08-06 | 6
2018-08-07 | 7
2018-08-08 | 8
2018-08-09 | 9
2018-08-10 | 10
Thank you!
You may need a row generator like the following:
select date '2018-08-01' + level -1 as yourDate,
level as yourRowNum
from dual
connect by date '2018-08-01' + level -1 <= date '2018-08-10'
The result:
YOURDATE YOURROWNUM
---------- ----------
2018-08-01 1
2018-08-02 2
2018-08-03 3
2018-08-04 4
2018-08-05 5
2018-08-06 6
2018-08-07 7
2018-08-08 8
2018-08-09 9
2018-08-10 10
To avoid repeating the date values, you can use:
with dateRange(startDate, endDate) as
(
select date '2018-08-01', date '2018-08-10'
from dual
)
select startDate + level -1 as yourDate,
level as yourRowNum
from dateRange
connect by startDate + level -1 <= endDate;
You may easily get what you'd like by using sum(1) over (order by "date"):
select "Date", sum(1) over (order by "Date") "Row Number"
from tab
where "Date" between date'2018-08-01' and date'2018-08-10';
Date Row Number
---------- ----------
2018-08-01 1
2018-08-02 2
2018-08-03 3
2018-08-04 4
2018-08-05 5
2018-08-06 6
2018-08-07 7
2018-08-08 8
2018-08-09 9
2018-08-10 10
Other alternatives count(1), row_number() might be replaced with sum(1), also.
SQL Fiddle Demo
I guess you want something like this...
WITH my_table AS
(SELECT TRUNC(SYSDATE) + LEVEL - 1 AS current_day
FROM DUAL
CONNECT BY LEVEL < 10)
SELECT FIRST_VALUE(current_day) OVER (ORDER BY current_day) first_day
, current_day
, current_day - FIRST_VALUE(current_day) OVER (ORDER BY current_day) days_diff
FROM my_TABLE;
FIRST_DAY CURRENT_DAY DAYS_DIFF
--------- ----------- ----------
29-AUG-18 29-AUG-18 0
29-AUG-18 30-AUG-18 1
29-AUG-18 31-AUG-18 2
29-AUG-18 01-SEP-18 3
29-AUG-18 02-SEP-18 4
29-AUG-18 03-SEP-18 5
29-AUG-18 04-SEP-18 6
29-AUG-18 05-SEP-18 7
29-AUG-18 06-SEP-18 8
9 rows selected.