I have a query where I want to know a person's current status.
Setup;
create table Person
(
personID int
,forename varchar2(50)
,surname varchar2(50)
);
insert all
into Person (personID, forename, surname) values (1,'AAA','BBB')
into Person (personID, forename, surname) values (2,'CCC','DDD')
select * from dual;
create table PersonStatus
(
personID int
,fromDate date
,status varchar2(5)
);
insert all
into PersonStatus (personID, fromDate, status) values (1,TO_DATE('2019-01-01','yyyy-mm-dd'),'Q')
into PersonStatus (personID, fromDate, status) values (1,TO_DATE('2019-02-01','yyyy-mm-dd'),'W')
into PersonStatus (personID, fromDate, status) values (1,TO_DATE('2019-03-01','yyyy-mm-dd'),'E')
into PersonStatus (personID, fromDate, status) values (2,TO_DATE('2019-01-01','yyyy-mm-dd'),'R')
into PersonStatus (personID, fromDate, status) values (2,TO_DATE('2019-02-01','yyyy-mm-dd'),'T')
into PersonStatus (personID, fromDate, status) values (2,TO_DATE('2019-03-01','yyyy-mm-dd'),'Y')
select * from dual;
So this query works fine;
select * from
(
select
p.personID
,p.foreName
,p.surName
,ps.fromDate
,ps.status
,row_number() over (partition by p.personID order by ps.fromDate desc) as rn
from Person p
left outer join PersonStatus ps on p.personID = ps.personID and ps.fromDate <= SYSDATE
) where rn = 1;
PERSONID FORENAME SURNAME FROMDATE STATU RN
---------- -------------------------------------------------- -------------------------------------------------- --------- ----- ----------
1 AAA BBB 01-MAR-19 E 1
2 CCC DDD 01-MAR-19 Y 1
So far so good!
The trouble starts as the report gets more complex. So, say I need to know a list of their issues and their status at the point they have an issue.
Additional setup;
create table PersonIssue
(
issueID int
,personID int
,issueDate date
);
insert all
into PersonIssue (issueID, personID, issueDate) values (1,1,TO_DATE('2019-01-14','yyyy-mm-dd'))
into PersonIssue (issueID, personID, issueDate) values (2,1,TO_DATE('2019-02-14','yyyy-mm-dd'))
into PersonIssue (issueID, personID, issueDate) values (3,1,TO_DATE('2019-03-14','yyyy-mm-dd'))
into PersonIssue (issueID, personID, issueDate) values (4,2,TO_DATE('2019-01-14','yyyy-mm-dd'))
into PersonIssue (issueID, personID, issueDate) values (5,2,TO_DATE('2019-02-14','yyyy-mm-dd'))
into PersonIssue (issueID, personID, issueDate) values (6,2,TO_DATE('2019-03-14','yyyy-mm-dd'))
select * from dual;
New query and results;
select * from
(
select
pi.issueID
,pi.issueDate
,p.personID
,p.foreName
,p.surName
,ps.fromDate
,ps.status
,row_number() over (partition by pi.issueID, p.personID order by ps.fromDate desc) as rn
from Person p
left outer join PersonIssue pi on p.personID = pi.personID
left outer join PersonStatus ps on p.personID = ps.personID and ps.fromDate <= pi.issueDate
order by p.personID, pi.issueDate, ps.fromDate desc
) where rn = 1;
ISSUEID ISSUEDATE PERSONID FORENAME SURNAME FROMDATE STATU RN
---------- --------- ---------- -------------------------------------------------- -------------------------------------------------- --------- ----- ----------
1 14-JAN-19 1 AAA BBB 01-JAN-19 Q 1
2 14-FEB-19 1 AAA BBB 01-FEB-19 W 1
3 14-MAR-19 1 AAA BBB 01-MAR-19 E 1
4 14-JAN-19 2 CCC DDD 01-JAN-19 R 1
5 14-FEB-19 2 CCC DDD 01-FEB-19 T 1
6 14-MAR-19 2 CCC DDD 01-MAR-19 Y 1
While this works, I have a couple of concerns about this which lead me to think I'm not doing this the right way.
Firstly, is the inequality join in the second query on PersonStatus potentially 'bringing back' lots of records I am not interested in only to be removed later on? Is there a more efficient way?
Secondly, I had to change the partition in the second query in order to get the correct results. Is there a way of doing this that better encapsulates that part of the logic so it does not have to change? I want to know a persons' status on a given date - it feels like I should somehow be able to do this in a more isolated way.
Thanks in advance!