I have a table like this
Emplid
REQUEST_ID
Status
Status_Dttm
1234
1
New
02-Jun-2022 12.35.00.AM
1231
5
Draft
02-Jun-2022 12.30.00.AM
1234
1
In Progress
02-Jun-2022 12.47.00.AM
1234
1
Cancelled
02-Jun-2022 12.50.00.AM
1234
2
New
03-Jun-2022 12.47.00.AM
Trying to create a view with the fields as EMPLID,REQUEST_ID,REQUEST_DATE,NO_OF_DAYS_IN_NEW,IN_PROGRESS_SINCE,COMPLETED/CANCELLED DATE
Tried using this
SELECT MIN(emplid) AS emplid, request_id, MIN(status_dttm) AS request_date,
MAX(status) KEEP (DENSE_RANK LAST ORDER BY status_dttm) AS current_status
FROM sts_tbl
GROUP BY request_id
CASE WHEN (MAX(status) KEEP (DENSE_RANK LAST ORDER BY status_dttm)) = 'NEW' THEN
TRUNC(SYSDATE) - MIN(status_dttm)
END AS "No of Days in New"
CASE WHEN (MAX(status) KEEP (DENSE_RANK LAST ORDER BY status_dttm)) = 'In Progree' THEN
status_dttm
END AS "In Progress Since"
FROM sts_tbl
GROUP BY emplid,request_id
getting error as "Not a Group By expression"
You can use the LEAD analytic function to find the time of the next status change and then aggregate:
SELECT MIN(emplid) AS emplid,
request_id,
MIN(status_dttm) AS request_date,
SUM(CASE status WHEN 'New' THEN duration ELSE 0 END) AS no_of_days_in_new,
MIN(CASE status WHEN 'In Progress' THEN status_dttm END)
AS in_progress_since,
MAX(status) KEEP (DENSE_RANK LAST ORDER BY status_dttm)
AS current_status,
MIN(CASE WHEN status IN ('Completed', 'Cancelled') THEN status_dttm END)
AS completed_cancelled_date
FROM (
SELECT t.*,
LEAD(status_dttm, 1, SYSDATE) OVER (
PARTITION BY request_id ORDER BY status_dttm
) - status_dttm AS duration
FROM sts_tbl t
)
GROUP BY request_id
Which, for the sample data:
CREATE TABLE sts_tbl (Emplid, REQUEST_ID, Status, Status_Dttm) AS
SELECT 1234, 1, 'Open', DATE '2022-06-02' + INTERVAL '35' MINUTE FROM DUAL UNION ALL
SELECT 1231, 5, 'Draft', DATE '2022-06-02' + INTERVAL '30' MINUTE FROM DUAL UNION ALL
SELECT 1234, 1, 'In Progress', DATE '2022-06-02' + INTERVAL '47' MINUTE FROM DUAL UNION ALL
SELECT 1234, 1, 'Cancelled', DATE '2022-06-02' + INTERVAL '50' MINUTE FROM DUAL UNION ALL
SELECT 1234, 2, 'New', DATE '2022-06-03' + INTERVAL '47' MINUTE FROM DUAL;
Outputs:
EMPLID
REQUEST_ID
REQUEST_DATE
NO_OF_DAYS_IN_NEW
IN_PROGRESS_SINCE
CURRENT_STATUS
COMPLETED_CANCELLED_DATE
1234
1
2022-06-02 00:35:00
0
2022-06-02 00:47:00
Cancelled
2022-06-02 00:50:00
1234
2
2022-06-03 00:47:00
80.54319444444444444444444444444444444444
null
New
null
1231
5
2022-06-02 00:30:00
0
null
Draft
null
Note: there is not a next status for request_id 2 so it is assumed that the number of days in the New status is from the start of the new status until the current date.
db<>fiddle here
Related
I have a problem how to calculate the days how many days has passed since previous order.
My code:
select
order_id,
order_date
from
oe.orders
where customer_id = 838
order by
order_date desc
The order_id and order_date are like below:
order_id = 1920 & order_date= 25-MAR-19 15.45.38.000000000
order_id = 1618 & order_date= 08-FEB-19 12.51.39.000000000
order_id = 1592 & order_date= 04-FEB-19 07.35.46.000000000
...
I am new user of sql and no idea how to do it. Thank you for your help!
If you want the differences in days (just the date part) then:
WITH
tbl AS
(
Select 1 "ID", To_Date('25-MAR-19 15.45.38', 'dd-MON-yy hh24:mi:ss') "A_DATE" From Dual Union All
Select 2 "ID", To_Date('08-FEB-19 12.51.39', 'dd-MON-yy hh24:mi:ss') "A_DATE" From Dual Union All
Select 3 "ID", To_Date('04-FEB-19 07.35.46', 'dd-MON-yy hh24:mi:ss') "A_DATE" From Dual Union All
Select 4 "ID", To_Date('28-JAN-19 12.13.10', 'dd-MON-yy hh24:mi:ss') "A_DATE" From Dual
)
Select
ID "ID",
TRUNC(A_DATE, 'dd') - TRUNC(Nvl(First_Value(A_DATE) OVER (Order By ID Rows Between 1 Preceding And Current Row), A_DATE), 'dd') "DAYS_DIFF"
From
tbl
ID
DAYS_DIFF
1
0
2
-45
3
-4
4
-7
OR ...
Select
ID "ID",
TRUNC(A_DATE, 'dd') - TRUNC(Nvl(Last_Value(A_DATE) OVER (Order By ID Rows Between Current Row And 1 Following ), A_DATE), 'dd') "DAYS_DIFF"
From
tbl
Order By TRUNC(A_DATE, 'dd')
... result
ID
DAYS_DIFF
4
0
3
7
2
4
1
45
Regards
CREATE TABLE orders
(ORDER_ID, ORDER_DATE) AS
SELECT 3, TIMESTAMP'2022-10-31 09:54:48' FROM DUAL UNION ALL
SELECT 2, TIMESTAMP'2022-10-17 19:04:44' FROM DUAL UNION ALL
SELECT 1, TIMESTAMP'2022-10-08 14:44:23' FROM DUAL
SELECT order_id, order_date,
order_date - LAG(order_date) OVER (ORDER BY order_id) AS diff
FROM orders;
ORDER_ID ORDER_DATE DIFF
1 08-OCT-22 02.44.23.000000 PM -
2 17-OCT-22 07.04.44.000000 PM +000000009 04:20:21.000000000
3 31-OCT-22 09.54.48.000000 AM +000000013 14:50:04.000000000
I need SELECT for finding data with overlapping date in Oracle SQL just from today to exactly one year ago. ID_FORMULAR is not UNIQUE value and I need to include just data with overlapping date where ID_FORMULAR is UNIQUE.
My code:
SELECT T1.*
FROM VISITORS T1, VISITORS T2
WHERE ( T1.ID_FORMULAR != T2.ID_FORMULAR
AND t1.FROM_DATE >= t2.FROM_DATE
AND t1.FROM_DATE <= t2.TO_DATE
AND T1.CREATED_DATE >= ADD_MONTHS (TRUNC (CURRENT_DATE), -12)
AND T1.CREATED_DATE < TRUNC (CURRENT_DATE) + 1)
OR ( T1.ID_FORMULAR != T2.ID_FORMULAR
AND t1.TO_DATE >= t2.FROM_DATE
AND t1.TO_DATE <= t2.TO_DATE
AND T1.CREATED_DATE >= ADD_MONTHS (TRUNC (CURRENT_DATE), -12)
AND T1.CREATED_DATE < TRUNC (CURRENT_DATE) + 1)
OR ( T1.ID_FORMULAR != T2.ID_FORMULAR
AND t1.TO_DATE >= t2.TO_DATE
AND t1.FROM_DATE <= t2.FROM_DATE
AND T1.CREATED_DATE >= ADD_MONTHS (TRUNC (CURRENT_DATE), -12)
AND T1.CREATED_DATE < TRUNC (CURRENT_DATE) + 1)
It is not working correctly. Any help?
From Oracle 12, you can use MATCH_RECOGNIZE to perform row-by-row processing:
SELECT *
FROM (
SELECT *
FROM visitors
WHERE created_date >= ADD_MONTHS(TRUNC(CURRENT_DATE), -12)
AND created_date < TRUNC(CURRENT_DATE) + 1
)
MATCH_RECOGNIZE(
ORDER BY from_date
ALL ROWS PER MATCH
PATTERN (any_row overlap+)
DEFINE
overlap AS PREV(id_formular) != id_formular
AND PREV(to_date) >= from_date
)
Which, for the sample data:
CREATE TABLE visitors (id_formular, created_date, from_date, to_date) AS
SELECT 1, DATE '2022-08-01', DATE '2022-08-01', DATE '2022-08-03' FROM DUAL UNION ALL
SELECT 2, DATE '2022-08-01', DATE '2022-08-02', DATE '2022-08-04' FROM DUAL UNION ALL
SELECT 3, DATE '2022-08-01', DATE '2022-08-03', DATE '2022-08-05' FROM DUAL UNION ALL
SELECT 1, DATE '2022-08-01', DATE '2022-08-06', DATE '2022-08-06' FROM DUAL UNION ALL
SELECT 2, DATE '2022-08-01', DATE '2022-08-07', DATE '2022-08-09' FROM DUAL UNION ALL
SELECT 2, DATE '2022-08-01', DATE '2022-08-08', DATE '2022-08-10' FROM DUAL UNION ALL
SELECT 1, DATE '2022-08-01', DATE '2022-08-09', DATE '2022-08-11' FROM DUAL;
Outputs:
FROM_DATE
ID_FORMULAR
CREATED_DATE
TO_DATE
01-AUG-22
1
01-AUG-22
03-AUG-22
02-AUG-22
2
01-AUG-22
04-AUG-22
03-AUG-22
3
01-AUG-22
05-AUG-22
08-AUG-22
2
01-AUG-22
10-AUG-22
09-AUG-22
1
01-AUG-22
11-AUG-22
db<>fiddle here
I don't quite understand the question. The thing that is confusing me is that you need just rows where ID is unique. If ID is unique than there is no other row to overlap with. Anyway, lets suppose that the sample data is like below:
WITH
tbl AS
(
SELECT 0 "ID", DATE '2021-07-01' "CREATED", DATE '2021-07-01' "DATE_FROM", DATE '2021-07-13' "DATE_TO" FROM DUAL UNION ALL
SELECT 1, DATE '2021-12-01', DATE '2021-12-01', DATE '2021-12-03' FROM DUAL UNION ALL
SELECT 1, DATE '2021-12-04', DATE '2021-12-04', DATE '2021-12-14' FROM DUAL UNION ALL
SELECT 1, DATE '2021-12-12', DATE '2021-12-12', DATE '2021-12-29' FROM DUAL UNION ALL
SELECT 2, DATE '2022-08-04', DATE '2022-08-04', DATE '2022-08-10' FROM DUAL UNION ALL
SELECT 2, DATE '2022-08-11', DATE '2022-08-11', DATE '2022-08-21' FROM DUAL UNION ALL
SELECT 2, DATE '2022-08-21', DATE '2022-08-21', DATE '2022-08-29' FROM DUAL UNION ALL
SELECT 3, DATE '2022-08-11', DATE '2022-08-11', DATE '2022-08-29' FROM DUAL UNION ALL
SELECT 4, DATE '2022-08-14', DATE '2022-08-14', DATE '2022-08-14' FROM DUAL UNION ALL
SELECT 4, DATE '2022-08-29', DATE '2022-08-14', DATE '2022-08-29' FROM DUAL
)
We can add some columns that will tell us if the ID is unique or not, what is the order of appearance of the same ID, what is the end date of the previous row for the same ID and if the rows of a particular ID overlaps or not. Here is the code: (used analytic functions with windowing clause)
SELECT
ID "ID",
CASE WHEN Count(*) OVER (PARTITION BY ID ORDER BY ID) = 1 THEN 'Y' ELSE 'N' END "IS_UNIQUE",
Count(ID) OVER (PARTITION BY ID ORDER BY ID, DATE_FROM, DATE_TO ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) "ID_ORDER_NO",
CREATED "CREATED",
DATE_FROM "DATE_FROM",
DATE_TO "DATE_TO",
CASE
WHEN Count(ID) OVER (PARTITION BY ID ORDER BY ID, DATE_FROM, DATE_TO ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) = 1
THEN Null
ELSE
First_Value(DATE_TO) OVER (PARTITION BY ID ORDER BY ID, DATE_FROM, DATE_TO ROWS BETWEEN 1 PRECEDING AND CURRENT ROW )
END "PREVIOUS_END_DATE",
CASE
WHEN Count(ID) OVER (PARTITION BY ID ORDER BY ID, DATE_FROM, DATE_TO ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) = 1
THEN 'N'
ELSE
CASE
WHEN DATE_FROM <= First_Value(DATE_TO) OVER (PARTITION BY ID ORDER BY ID, DATE_FROM, DATE_TO ROWS BETWEEN 1 PRECEDING AND CURRENT ROW )
THEN 'Y'
ELSE 'N'
END
END "OVERLAPS"
FROM
TBL
WHERE
CREATED BETWEEN ADD_MONTHS(TRUNC(SYSDATE, 'dd'), -12) And TRUNC(SYSDATE, 'dd')
Here is the resulting dataset...
/* R e s u l t
ID IS_UNIQUE ID_ORDER_NO CREATED DATE_FROM DATE_TO PREVIOUS_END_DATE OVERLAPS
---------- --------- ----------- --------- --------- --------- ----------------- --------
1 N 1 01-DEC-21 01-DEC-21 03-DEC-21 N
1 N 2 04-DEC-21 04-DEC-21 14-DEC-21 03-DEC-21 N
1 N 3 12-DEC-21 12-DEC-21 29-DEC-21 14-DEC-21 Y
2 N 1 04-AUG-22 04-AUG-22 10-AUG-22 N
2 N 2 11-AUG-22 11-AUG-22 21-AUG-22 10-AUG-22 N
2 N 3 21-AUG-22 21-AUG-22 29-AUG-22 21-AUG-22 Y
3 Y 1 11-AUG-22 11-AUG-22 29-AUG-22 N
4 N 1 14-AUG-22 14-AUG-22 14-AUG-22 N
4 N 2 29-AUG-22 14-AUG-22 29-AUG-22 14-AUG-22 Y
*/
This dataset could be further used to get you the rows and columns that you are trying to get. You can filter it, do some other calculations (like number of overlaping days), get number of rows per ID and so on....
Regards...
Here is one table name called..
COMPLAINTS :-
COMNO CDATE MESSAGE STATUS
----------------------------------------------------------------------
12345 05-JAN-22 CUSTOMER ISSUE REPORTED OPEN
12345 07-JAN-22 WAITING FOR THE CUSTOMER RESPONCE IN PROGRESS
12345 09-JAN-22 ISSUE RESOLVED CLOSED
56789 14-JAN-22 CUSTOMER ISSUE REPORTED OPEN
56789 23-JAN-22 ISSUE RESOLVED CLOSED
85642 03-JAN-22 CUSTOMER ISSUE REPORTED OPEN
78632 30-JAN-22 CUSTOMER ISSUE REPORTED OPEN
78632 31-JAN-22 WAITING FOR THE CUSTOMER RESPONCE IN PROGRESS
Here is my oracle query -
SELECT COMNO,
MIN(CDATE) AS COMPLAINT_CREATION_DATE,
MAX(CDATE) AS COMPLAINT_CLOSURE_DATE
FROM COMPLAINT
WHERE CDATE BETWEEN TO_DATE('01/Jan/2022 00:00:00','dd/Mon/yyyy hh24:mi:ss') AND TO_DATE('31/Jan/2022 23:59:59','dd/Mon/yyyy hh24:mi:ss')
AND REFNO='12345'
GROUP BY COMNO
which gives me the result of REFNO='12345'
COMNO COMPLAINT_CREATION_DATE COMPLAINT_CLOSURE_DATE
--------------------------------------------------------
12345 05-JAN-22 09-JAN-22
In this query i want to add the last status and message of all complaints
COMNO + COMPLAINT_CREATION_DATE + COMPLAINT_CLOSURE_DATE + MESSAGE + STATUS
-------------------------------------------------------------------
12345 05-JAN-22 09-JAN-22 ISSUE RESOLVED CLOSED
56789 14-JAN-22 23-JAN-22 ISSUE RESOLVED CLOSED
85642 03-JAN-22 03-JAN-22 CUSTOMER ISSUE REPORTED OPEN
78632 30-JAN-22 31-JAN-22 WAITING FOR THE CUSTOMER RESPONCE IN PROGRESS
Can anyone please help me on that.
Don't use a self-join, use KEEP (DENSE_RANK LAST ORDER BY CDATE):
SELECT COMNO,
MIN(CDATE) AS COMPLAINT_CREATION_DATE,
MAX(CDATE) AS COMPLAINT_CLOSURE_DATE,
MAX(MESSAGE) KEEP (DENSE_RANK LAST ORDER BY CDATE) AS message,
MAX(STATUS) KEEP (DENSE_RANK LAST ORDER BY CDATE) AS status
FROM COMPLAINT
WHERE CDATE >= DATE '2022-01-01'
AND CDATE < DATE '2022-02-01'
AND REFNO='12345'
GROUP BY COMNO
Or analytic functions:
SELECT comno,
COMPLAINT_CREATION_DATE,
cdate AS COMPLAINT_CLOSURE_DATE,
message,
status
FROM (
SELECT c.*,
MIN(CDATE) OVER (PARTITION BY comno) AS COMPLAINT_CREATION_DATE,
ROW_NUMBER() OVER (PARTITION BY comno ORDER BY cdate DESC) AS rn
FROM COMPLAINT c
WHERE CDATE >= DATE '2022-01-01'
AND CDATE < DATE '2022-02-01'
AND REFNO='12345'
)
WHERE rn = 1
Which, for the sample data:
CREATE TABLE complaint (REFNO, COMNO, CDATE, MESSAGE, STATUS) AS
SELECT '12345', 12345, DATE '2022-01-05', 'CUSTOMER ISSUE REPORTED', 'OPEN' FROM DUAL UNION ALL
SELECT '12345', 12345, DATE '2022-01-07', 'WAITING FOR THE CUSTOMER RESPONCE', 'IN PROGRESS' FROM DUAL UNION ALL
SELECT '12345', 12345, DATE '2022-01-09', 'ISSUE RESOLVED', 'CLOSED' FROM DUAL UNION ALL
SELECT '12345', 56789, DATE '2022-01-14', 'CUSTOMER ISSUE REPORTED', 'OPEN' FROM DUAL UNION ALL
SELECT '12345', 56789, DATE '2022-01-23', 'ISSUE RESOLVED', 'CLOSED' FROM DUAL UNION ALL
SELECT '12345', 85642, DATE '2022-01-03', 'CUSTOMER ISSUE REPORTED', 'OPEN' FROM DUAL UNION ALL
SELECT '12345', 78632, DATE '2022-01-30', 'CUSTOMER ISSUE REPORTED', 'OPEN' FROM DUAL UNION ALL
SELECT '12345', 78632, DATE '2022-01-31', 'WAITING FOR THE CUSTOMER RESPONCE', 'IN PROGRESS' FROM DUAL
Both output:
COMNO
COMPLAINT_CREATION_DATE
COMPLAINT_CLOSURE_DATE
MESSAGE
STATUS
12345
2022-01-05 00:00:00
2022-01-09 00:00:00
ISSUE RESOLVED
CLOSED
56789
2022-01-14 00:00:00
2022-01-23 00:00:00
ISSUE RESOLVED
CLOSED
78632
2022-01-30 00:00:00
2022-01-31 00:00:00
WAITING FOR THE CUSTOMER RESPONCE
IN PROGRESS
85642
2022-01-03 00:00:00
2022-01-03 00:00:00
CUSTOMER ISSUE REPORTED
OPEN
db<>fiddle here
You can remove the filter condition form your query and join it again to the table -
SELECT C.COMNO,
CD.COMPLAINT_CREATION_DATE,
CD.COMPLAINT_CLOSURE_DATE,
C.MESSAGE,
C.STATUS
FROM (SELECT COMNO,
MIN(CDATE) AS COMPLAINT_CREATION_DATE,
MAX(CDATE) AS COMPLAINT_CLOSURE_DATE
FROM COMPLAINT
WHERE CDATE BETWEEN TO_DATE('01/Jan/2022 00:00:00','dd/Mon/yyyy hh24:mi:ss')
AND TO_DATE('31/Jan/2022 23:59:59','dd/Mon/yyyy hh24:mi:ss')
GROUP BY COMNO) CD
JOIN COMPLAINTS C ON CD.COMNO = C.COMNO
AND CD.COMPLAINT_CLOSURE_DATE = C.CDATE;
Demo.
I have a scenario, where I have to exclude duplicates and capture only the changes. Also calculate the valid_from and valid_to on the fly. I have tried a query and it works but it is very slow in performance and it is failing with memory error .
Input : Only capture Entries where there is a change either in Amount/Check-In-Out.
Calculate Valid_from and Valid_to based on Date Changed.
Output:
SQL I tried.
select * from (select
lead(start_date, "window_offset" - rn + 1, '9999-12-31') over (order by "grp" ) as valid_to,
case when rn = max(rn) over (partition by "grp") then 1 else 0 end as "isLastUpdate",
start_date as valid_from,*
from (
select
min("DateChanged") over (partition by "grp") as start_date,
count(*) over (partition by "grp") as "window_offset",
row_number() over (partition by "grp" order by "DateChanged") as rn,
*
from (
select sum("isChanged") over (partition by OrderId order by "DateChanged") as "grp",*
from (
select
case when "Amount" = lag( "Amount" ) over (partition by OrderId order by "DateChanged") and
"Check-In" = lag( "Check-In" ) over (partition by OrderId order by "DateChanged") and
"Check-Out" = lag( "Check-Out" ) over (partition by OrderId order by "DateChanged")
then 0
else 1
end "isChanged",
*
FROM :in_table
)
))
where "isLastUpdate" = 1;
The logic of your expected answer is unclear as to why you get valid_from as 8-mar-21 for the first order_id and 9-apr-21 for the second order_id as both order_ids have overlapping ranges but you take the least of the previous check_out and the next check_in for the first order_id and the greatest of those two for the second and it is inconsistent.
If you want to get valid_from as the greatest of either the current check_in or the previous check_outs and valid_to as the greatest of either the current check_out or the next check_in or, if there are no more rows, 9999-12-31 then:
SELECT orderid,
amount,
check_in,
check_out,
GREATEST(
check_in,
COALESCE(
MAX(check_out) OVER (
PARTITION BY orderid
ORDER BY check_in, check_out
ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
),
check_in
)
) AS valid_from,
GREATEST(
check_out,
LEAD(check_in, 1, DATE '9999-12-31') OVER (
PARTITION BY orderid ORDER BY check_in, check_out
)
) AS valid_to
FROM (
SELECT DISTINCT *
FROM table_name
)
Which, for the sample data:
CREATE TABLE table_name (orderid, datechanged, amount, check_in, check_out) AS
SELECT 1, DATE '2021-03-3', 12.12, DATE '2021-03-03', DATE '2021-03-10' FROM DUAL UNION ALL
SELECT 1, DATE '2021-03-3', 12.12, DATE '2021-03-03', DATE '2021-03-10' FROM DUAL UNION ALL
SELECT 1, DATE '2021-03-3', 12.12, DATE '2021-03-03', DATE '2021-03-10' FROM DUAL UNION ALL
SELECT 1, DATE '2021-03-8', 21.12, DATE '2021-03-08', DATE '2021-03-18' FROM DUAL UNION ALL
SELECT 1, DATE '2021-03-8', 21.12, DATE '2021-03-08', DATE '2021-03-18' FROM DUAL UNION ALL
SELECT 2, DATE '2021-04-4', 9.10, DATE '2021-04-04', DATE '2021-04-09' FROM DUAL UNION ALL
SELECT 2, DATE '2021-04-4', 9.10, DATE '2021-04-04', DATE '2021-04-09' FROM DUAL UNION ALL
SELECT 2, DATE '2021-04-4', 10.20, DATE '2021-04-04', DATE '2021-04-12' FROM DUAL;
Outputs:
ORDERID
AMOUNT
CHECK_IN
CHECK_OUT
VALID_FROM
VALID_TO
1
12.12
2021-03-03 00:00:00
2021-03-10 00:00:00
2021-03-03 00:00:00
2021-03-10 00:00:00
1
21.12
2021-03-08 00:00:00
2021-03-18 00:00:00
2021-03-10 00:00:00
9999-12-31 00:00:00
2
9.1
2021-04-04 00:00:00
2021-04-09 00:00:00
2021-04-04 00:00:00
2021-04-09 00:00:00
2
10.2
2021-04-04 00:00:00
2021-04-12 00:00:00
2021-04-09 00:00:00
9999-12-31 00:00:00
db<>fiddle here
I have a table with project, period and status with their periodic values. My goal is to show how many months have passed since certain project was last in 'Approved status' (or show what period this status was changed) in the period_leaving_approved_status column. for example, for periods 201005 to 201008 - i would need to show '201005' values, but for periods 201011-201012 - '201011' values. I managed to mark the line where status changes, but i don't know how to apply the condition for the following rows. My example query :
with subq as (
select 123 as project, 201002 as period, 'Approved' as status from dual
union all
select 123 as project, 201003 as period, 'Approved' as status from dual
union all
select 123 as project, 201004 as period, 'Approved' as status from dual
union all
select 123 as project, 201005 as period, 'Pending Close' as status from dual
union all
select 123 as project, 201006 as period, 'Pending Close' as status from dual
union all
select 123 as project, 201007 as period, 'Closed' as status from dual
union all
select 123 as project, 201008 as period, 'Closed' as status from dual
union all
select 123 as project, 201009 as period, 'Approved' as status from dual
union all
select 123 as project, 201010 as period, 'Approved' as status from dual
union all
select 123 as project, 201011 as period, 'Closed' as status from dual
union all
select 123 as project, 201012 as period, 'Closed' as status from dual
union all
select 123 as project, 201101 as period, 'Approved' as status from dual
union all
select 123 as project, 201102 as period, 'Approved' as status from dual
union all
select 123 as project, 201112 as period, 'Approved' as status from dual
union all
select 123 as project, 201301 as period, 'Pending Close' as status from dual
union all
select 123 as project, 201302 as period, 'Closed' as status from dual
union all
select 123 as project, 201203 as period, 'Closed' as status from dual
)
select project,
period,
status,
case when lag(status, 1, null) OVER (ORDER BY period)='Approved'
AND lag(status, 1, null) OVER (ORDER BY period) NOT IN(status) then period end as period_leaving_approved_status
from subq
I would approach this using aggregation:
select project,
max(case when status = 'Approved' then period end) as ApprovedPeriod
from subq;
If you want the time span then something like this:
select project,
months_between(max(case when status = 'Approved' then period end),
sysdate) as monthsSinceApproved,
max(case when status = 'Approved' then period end) as ApprovedPeriod
from subq
group by project;
EDIT:
If you want this information cumulatively on all rows, then use window functions:
select s.*,
max(case when status = 'Approved' then period end) over
(partition b project order by period) as LastApprovedPeriod
from subq s;