Order Data based on previous row data - sql

I have a query in Oracle SQL. My query gives three columns: old_data ,new_data and transaction_date. I want to sort this data primarily based on increasing transaction_date and secondly in such a way that new_data of previous row equals old_data of next row. Both new_data and old_date are number fields that can decrease or increase.
If I sort just by transaction_date, some data has the same exact date and time and hence the order will not be accurate as I need new_data of previous row to match old_data of current row. I also cannot use a hierarchical query alone to meet the second sorting condition since transaction_date sorting is the primary sorting condition.
Can anyone suggest a solution?
A sample output will need to look like below:
output_sample
Thanks in advance

You could use a hierarchical query and connect by equal dates as well as the relationship between old- and new-data:
SELECT transaction_date,
new_data,
old_data
FROM table_name
START WITH old_data IS NULL -- You need to define how to pick the first row
CONNECT BY
PRIOR transaction_date = transaction_date
AND PRIOR new_data = old_date
ORDER SIBLINGS BY
transaction_date
Which, for the sample data:
ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';
CREATE TABLE table_name ( transaction_date, new_data, old_data ) AS
SELECT DATE '2022-01-01', 1, NULL FROM DUAL UNION ALL
SELECT DATE '2022-01-01', 2, 1 FROM DUAL UNION ALL
SELECT DATE '2022-01-01', 3, 2 FROM DUAL UNION ALL
SELECT DATE '2022-01-02', 3, NULL FROM DUAL UNION ALL
SELECT DATE '2022-01-02', 1, 3 FROM DUAL UNION ALL
SELECT DATE '2022-01-02', 2, 1 FROM DUAL UNION ALL
SELECT DATE '2022-01-03', 4, NULL FROM DUAL;
Outputs:
TRANSACTION_DATE
NEW_DATA
OLD_DATA
2022-01-01 00:00:00
1
null
2022-01-01 00:00:00
2
1
2022-01-01 00:00:00
3
2
2022-01-02 00:00:00
3
null
2022-01-02 00:00:00
1
3
2022-01-02 00:00:00
2
1
2022-01-03 00:00:00
4
null
fiddle
Update
Given the updated sample data:
CREATE TABLE table_name ( transaction_date, old_data, new_data ) AS
SELECT DATE '2021-12-20'+INTERVAL '11:25' HOUR TO MINUTE, 0, 115.09903 FROM DUAL UNION ALL
SELECT DATE '2021-12-20'+INTERVAL '11:25' HOUR TO MINUTE, 115.09903, 115.13233 FROM DUAL UNION ALL
SELECT DATE '2021-12-20'+INTERVAL '11:25' HOUR TO MINUTE, 115.13233, 115.16490 FROM DUAL UNION ALL
SELECT DATE '2021-12-20'+INTERVAL '11:25' HOUR TO MINUTE, 115.16490, 115.19678 FROM DUAL UNION ALL
SELECT DATE '2021-12-20'+INTERVAL '11:35' HOUR TO MINUTE, 115.19678, 115.22799 FROM DUAL UNION ALL
SELECT DATE '2021-12-20'+INTERVAL '11:35' HOUR TO MINUTE, 115.22799, 115.25854 FROM DUAL UNION ALL
SELECT DATE '2021-12-20'+INTERVAL '11:35' HOUR TO MINUTE, 115.25854, 115.28846 FROM DUAL UNION ALL
SELECT DATE '2021-12-20'+INTERVAL '11:35' HOUR TO MINUTE, 115.28846, 115.31776 FROM DUAL;
Then, since both old_data and new_data increase with time, you could use:
SELECT *
FROM table_name
ORDER BY old_data;
or:
SELECT *
FROM table_name
ORDER BY new_data;
or, if you want to use the hierarchy then:
SELECT transaction_date,
old_data,
new_data
FROM table_name
START WITH old_data = 0
CONNECT BY
PRIOR transaction_date <= transaction_date
AND PRIOR new_data = old_data
ORDER SIBLINGS BY
transaction_date
Which all output:
TRANSACTION_DATE
OLD_DATA
NEW_DATA
2021-12-20 11:25:00
0
115.09903
2021-12-20 11:25:00
115.09903
115.13233
2021-12-20 11:25:00
115.13233
115.1649
2021-12-20 11:25:00
115.1649
115.19678
2021-12-20 11:35:00
115.19678
115.22799
2021-12-20 11:35:00
115.22799
115.25854
2021-12-20 11:35:00
115.25854
115.28846
2021-12-20 11:35:00
115.28846
115.31776
fiddle

Related

Oracle SQL Check current rows with previous rows dynamically

I am trying to work on something in Oracle SQL in the attached table.
The goal is to define a Visit Type where a Primary Visit refers to any visits that happened after 3 months from previous visit(s). However, the challenge is that sometimes I'll need to compare to previous row and sometimes I need to compare with previous N rows.
For e.g., Transaction ID 3 is a revisit because its start date is within the 'end date +90 days' of Transaction ID 1 (Dec 16). Transaction ID 4 is primary because it happened after it took place after the the very first 'end date+90 days', meaning I not only need to compare to previous 1 row but previous 3 rows.
Hope this is clear!
Thanks.
See Details above. Thank you!
From Oracle 12 you can use MATCH_RECOGNIZE for row-by-row pattern matching:
SELECT transaction_id, start_date, end_date, visit_type
FROM table_name
MATCH_RECOGNIZE(
ORDER BY start_date
MEASURES
CLASSIFIER() AS visit_type
ALL ROWS PER MATCH
PATTERN (PRIMARY revisit*)
DEFINE revisit AS start_date <= FIRST(end_date) + INTERVAL '90' DAY
);
Which, for the sample data:
CREATE TABLE table_name (transaction_id, start_date, end_date) AS
SELECT 1, DATE '2020-08-05', DATE '2020-09-07' FROM DUAL UNION ALL
SELECT 2, DATE '2020-09-19', DATE '2020-10-27' FROM DUAL UNION ALL
SELECT 3, DATE '2020-11-01', DATE '2020-12-19' FROM DUAL UNION ALL
SELECT 4, DATE '2021-01-23', DATE '2021-01-26' FROM DUAL UNION ALL
SELECT 5, DATE '2021-02-27', DATE '2021-03-27' FROM DUAL;
Outputs:
TRANSACTION_ID
START_DATE
END_DATE
VISIT_TYPE
1
2020-08-05 00:00:00
2020-09-07 00:00:00
PRIMARY
2
2020-09-19 00:00:00
2020-10-27 00:00:00
REVISIT
3
2020-11-01 00:00:00
2020-12-19 00:00:00
REVISIT
4
2021-01-23 00:00:00
2021-01-26 00:00:00
PRIMARY
5
2021-02-27 00:00:00
2021-03-27 00:00:00
REVISIT

Query to get record list as per particular column data

I want active ids but not those record which have I' as status till next record for that id but if the previous record has status 'A' then it should come but not after the status 'I record'.
INPUT:
id
start_date
end_date
status
1000000278
8/25/2021
8/25/2022
I
1000000278
8/25/2022
8/25/2023
A
1000000284
8/20/2021
8/25/2022
A
1000000284
8/25/2022
8/25/2023
A
1000000285
8/20/2024
8/20/2028
A
1000000285
8/21/2028
8/20/2030
I
1000000285
8/21/2030
8/20/2031
A
1000000286
8/25/2021
8/25/2022
A
OUTPUT:
id
start_date
end_date
status
1000000284
8/20/2021
8/25/2022
A
1000000284
8/25/2022
8/25/2023
A
1000000285
8/20/2024
8/20/2028
A
1000000286
8/25/2021
8/25/2022
A
From Oracle 12, you can use MATCH_RECOGNIZE to perform row-by-row processing. To get the rows for each id with the earliest status of A until the first status I row, you can use:
SELECT *
FROM table_name
MATCH_RECOGNIZE(
PARTITION BY id
ORDER BY start_date
ALL ROWS PER MATCH
PATTERN ( ^ a_status+ )
DEFINE a_status AS status = 'A'
)
Which, for the sample data:
CREATE TABLE table_name (id, start_date, end_date, status) AS
SELECT 1000000278, DATE '2021-08-25', DATE '2022-08-25', 'I' FROM DUAL UNION ALL
SELECT 1000000278, DATE '2022-08-25', DATE '2023-08-25', 'A' FROM DUAL UNION ALL
SELECT 1000000284, DATE '2021-08-20', DATE '2022-08-25', 'A' FROM DUAL UNION ALL
SELECT 1000000284, DATE '2022-08-25', DATE '2023-08-25', 'A' FROM DUAL UNION ALL
SELECT 1000000285, DATE '2024-08-20', DATE '2028-08-20', 'A' FROM DUAL UNION ALL
SELECT 1000000285, DATE '2028-08-21', DATE '2030-08-20', 'I' FROM DUAL UNION ALL
SELECT 1000000285, DATE '2030-08-21', DATE '2031-08-20', 'A' FROM DUAL UNION ALL
SELECT 1000000286, DATE '2021-08-25', DATE '2022-08-25', 'A' FROM DUAL;
Outputs:
ID
START_DATE
END_DATE
STATUS
1000000284
2021-08-20 00:00:00
2022-08-25 00:00:00
A
1000000284
2022-08-25 00:00:00
2023-08-25 00:00:00
A
1000000285
2024-08-20 00:00:00
2028-08-20 00:00:00
A
1000000286
2021-08-25 00:00:00
2022-08-25 00:00:00
A
fiddle

Fetch record with max number in one column except if date in that column is > than today

I have a problem with fetching few exceptions from DB.
Example, table b:
sn
v_num
start_date
end_date
1
001
01-01-2019
31-12-2099
1
002
01-01-2021
31-01-2022
1
003
01-02-2022
31-12-2099
2
001
01-01-2022
31-12-2099
2
002
01-07-2022
31-07-2022
2
003
01-08-2022
31-12-2099
Expected output:
sn
v_num
start_date
end_date
1
003
01-02-2022
31-12-2099
2
001
01-01-2022
31-12-2099
Currently I'm here:
SELECT * FROM table a, table b
WHERE a.sn = b.sn
AND b.v_num = (SELECT max (v_num) FROM b WHERE a.sn = b.sn)
but obviously that is not good because of a few cases like this with sn = 2.
Conclusion, I need to get unique sn record where v_num is max (95% of them in DB) except in case if start_date of max v_num record is > today.
Filter using start_date <= TRUNC(SYSDATE) then use the ROW_NUMBER analytic function:
SELECT *
FROM (
SELECT a.*,
ROW_NUMBER() OVER (PARTITION BY sn ORDER BY v_num DESC) AS rn
FROM "TABLE" a
WHERE start_date <= TRUNC(SYSDATE)
)
WHERE rn = 1;
If the start_date has a time component then you can use start_date < TRUNC(SYSDATE) + INTERVAL '1' DAY to get all the values for today from 00:00:00 to 23:59:59.
If you can have ties for the maximum and want to return all the ties then you can use the RANK analytic function instead of ROW_NUMBER.
Which, for the sample data:
CREATE TABLE "TABLE" (sn, v_num, start_date, end_date) AS
SELECT 1, '001', DATE '2022-01-01', DATE '2099-12-31' FROM DUAL UNION ALL
SELECT 1, '002', DATE '2022-01-01', DATE '2022-01-31' FROM DUAL UNION ALL
SELECT 1, '003', DATE '2022-02-01', DATE '2099-12-31' FROM DUAL UNION ALL
SELECT 2, '001', DATE '2022-01-01', DATE '2099-12-31' FROM DUAL UNION ALL
SELECT 2, '002', DATE '2022-07-01', DATE '2022-07-31' FROM DUAL UNION ALL
SELECT 2, '003', DATE '2022-08-01', DATE '2099-12-31' FROM DUAL;
Outputs:
SN
V_NUM
START_DATE
END_DATE
RN
1
003
2022-02-01 00:00:00
2099-12-31 00:00:00
1
2
001
2022-01-01 00:00:00
2099-12-31 00:00:00
1
db<>fiddle here

Finding overlapping and calculation of min and max dates with in overlap in oracle [duplicate]

This question already has answers here:
print start and end date in one row for continous or overlaping date ranges in oracle SQL
(2 answers)
Closed 2 years ago.
How can we find the between the overlap lap b/w the dates .
overlap means when start date and end date are within same range
for below example row 1 has no over lap.
Row 2to 5 can be considered as one set of over lap as there start date and end are over lap with themselves
Row 6 & 7 can be considered as one set of over lap
for eg. row 6 & 7 --> start date of row 7 is in same range with respect to end date of row 6
so it becomes an overlap
Once overlap is found then and need to find out min(start date) and max(end date) and
want to assign a unique id to each overlap and in the S.NO column should show which rows are overlapped .Below is the I/p and O/p
I/p
You can do it simply and efficiently using MATCH_RECOGNIZE to perform a row-by-row comparison and aggregation:
SELECT id, start_date, end_date
FROM table_name
MATCH_RECOGNIZE (
PARTITION BY id
ORDER BY start_date
MEASURES FIRST(start_date) AS start_date,
MAX(end_date) AS end_date
PATTERN ( overlapping_dates* last_date )
DEFINE overlapping_dates as MAX(end_date) >= NEXT(start_date)
);
Which, for the sample data:
CREATE TABLE table_name ( sno, id, start_date, end_date ) AS
SELECT 1, 1, DATE '2019-10-11', DATE '2019-10-11' FROM DUAL UNION ALL
SELECT 2, 1, DATE '2019-11-04', DATE '2019-12-11' FROM DUAL UNION ALL
SELECT 3, 1, DATE '2019-11-05', DATE '2019-11-10' FROM DUAL UNION ALL
SELECT 4, 1, DATE '2019-11-06', DATE '2019-11-10' FROM DUAL UNION ALL
SELECT 5, 1, DATE '2019-11-20', DATE '2019-12-20' FROM DUAL UNION ALL
SELECT 6, 1, DATE '2020-01-01', DATE '2020-01-20' FROM DUAL UNION ALL
SELECT 7, 1, DATE '2020-01-15', DATE '2020-03-25' FROM DUAL;
Outputs:
ID | START_DATE | END_DATE
-: | :------------------ | :------------------
1 | 2019-10-11 00:00:00 | 2019-10-11 00:00:00
1 | 2019-11-04 00:00:00 | 2019-12-20 00:00:00
1 | 2020-01-01 00:00:00 | 2020-03-25 00:00:00
db<>fiddle here

Select dates older than time frame SQL

I am trying to find all records in a database with an admission date which is older than a certain time frame (in this case, all admission dates older than 4 days old).
I have:
select memberid, admitdate
from membertable
where admitdate < (sysdate-4)
As a result, I'm getting a lot of admission dates which match this, but I'm ALSO getting dates which are from only 2 days ago, so that doesn't match my code. What am I doing wrong?
If it helps, the admit dates have a format of mm/dd/yyyy.
Dates, including sysdate, have a time component. Even if all your admitdate values are at midnight that is still a time, and sysdate is only going to be at midnight if you run your query then.
select sysdate, sysdate-4, trunc(sysdate), trunc(sysdate)-4 from dual;
SYSDATE SYSDATE-4 TRUNC(SYSDATE) TRUNC(SYSDATE)-4
------------------- ------------------- ------------------- -------------------
2018-06-21 16:44:53 2018-06-17 16:44:53 2018-06-21 00:00:00 2018-06-17 00:00:00
If you filter your records on sysdate-4 then that will include any admitdate values up to, in this example, 2018-06-17 16:44:53; so presumably all the records for the 17th if they are actually all midnight.
with membertable (memberid, admitdate) as (
select 1, date '2018-06-15' from dual
union all select 2, date '2018-06-16' from dual
union all select 3, date '2018-06-17' from dual
union all select 4, date '2018-06-18' from dual
union all select 5, date '2018-06-19' from dual
union all select 6, date '2018-06-20' from dual
union all select 7, date '2018-06-21' from dual
)
select memberid, admitdate
from membertable
where admitdate < (sysdate-4);
MEMBERID ADMITDATE
---------- -------------------
1 2018-06-15 00:00:00
2 2018-06-16 00:00:00
3 2018-06-17 00:00:00
If you truncate the value you're comparing against then its time portion will also be treated as midnight, so you'll only match record up to - but not including - that point in time, 2018-06-17 00:00:00:
with membertable (memberid, admitdate) as (
select 1, date '2018-06-15' from dual
union all select 2, date '2018-06-16' from dual
union all select 3, date '2018-06-17' from dual
union all select 4, date '2018-06-18' from dual
union all select 5, date '2018-06-19' from dual
union all select 6, date '2018-06-20' from dual
union all select 7, date '2018-06-21' from dual
)
select memberid, admitdate
from membertable
where admitdate < trunc(sysdate)-4;
MEMBERID ADMITDATE
---------- -------------------
1 2018-06-15 00:00:00
2 2018-06-16 00:00:00
admitdate should be a date. You seem to be suggesting it is a string. You can try:
where to_date(admitdate, 'MM/DD/YYYY') < trunc(sysdate) - 4;
You can then fix the data in the table, so it is stored as a date.