Oracle 11g. Можно ли избежать увеличения сложности разделов при объединении таблицы «один ко многим»? - PullRequest
2 голосов
/ 25 октября 2019

У меня есть запрос, по которому я хочу узнать текущий статус человека.

Настройка;

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;

Так что этот запрос работает нормально;

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


Итакпока все хорошо!

Проблема начинается с усложнения отчета. Итак, скажем, мне нужно знать список их проблем и их статус в том месте, где у них есть проблема.

Дополнительная настройка;

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;

Новый запрос и результаты;

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

Хотя это работает, у меня есть пара опасений по этому поводу, которые заставляют меня думать, что я делаю это неправильно.

Во-первых, это соединение неравенства во втором запросе PersonStatusпотенциально «возвращение» большого количества записей, которые меня не интересуют, только для последующего удаления? Есть ли более эффективный способ?

Во-вторых, мне пришлось изменить раздел во втором запросе, чтобы получить правильные результаты. Есть ли способ сделать это, что лучше инкапсулирует эту часть логики, чтобы не пришлось менять? Я хочу знать статус человека на определенную дату - такое чувство, что я должен каким-то образом сделать это более изолированным способом.

Заранее спасибо!

1 Ответ

0 голосов
/ 26 октября 2019

Может быть, это поможет: {1} Напишите ВИД, который возвращает фактические даты от / до для каждого статуса. {2} Используйте ВНУТРЕННИЕ СОЕДИНЕНИЯ, чтобы получить необходимые данные из ваших таблиц.

{1} ПРОСМОТР

create or replace view status_view
as
select id as personid, status, from_
, case 
    when to_ is null then sysdate  -- latest TO_ date: SYSDATE
    else to_ - 1
  end to_
from (
  select 
    personid id
  , fromdate from_
  , lead( fromdate, 1 ) over ( partition by personid order by fromdate ) to_
  , status status
  from personstatus 
) ;

При ВЫБОРЕ из вида вы 'Вы получите:

select * from status_view ;

  PERSONID STATUS   FROM_       TO_         
         1 Q        01-JAN-19   31-JAN-19   
         1 W        01-FEB-19   28-FEB-19   
         1 E        01-MAR-19   26-OCT-19   
         2 R        01-JAN-19   31-JAN-19   
         2 T        01-FEB-19   28-FEB-19   
         2 Y        01-MAR-19   26-OCT-19   

6 rows selected.

{2} ВНУТРЕННИЕ СОЕДИНЕНИЯ

select 
  p.personID, p.foreName || ' ' || p.surName as person
, pi.issueID as iid
, ps.from_  as statusfrom
, pi.issuedate     -- issue date in between FROM_ and TO_ (easier to see ...)
, ps.to_ as statusto
, ps.status
from Person p
  join PersonIssue pi 
    on p.personID = pi.personID
  join status_view ps 
    on p.personID = ps.personid
    and pi.issuedate between ps.from_ and ps.to_
order by p.personid, pi.issuedate
;

РЕЗУЛЬТАТ

  PERSONID PERSON      IID STATUSFROM   ISSUEDATE   STATUSTO    STATUS   
         1 AAA BBB       1 01-JAN-19    14-JAN-19   31-JAN-19   Q        
         1 AAA BBB       2 01-FEB-19    14-FEB-19   28-FEB-19   W        
         1 AAA BBB       3 01-MAR-19    14-MAR-19   26-OCT-19   E        
         2 CCC DDD       4 01-JAN-19    14-JAN-19   31-JAN-19   R        
         2 CCC DDD       5 01-FEB-19    14-FEB-19   28-FEB-19   T        
         2 CCC DDD       6 01-MAR-19    14-MAR-19   26-OCT-19   Y 

Вы можете(конечно) напишите код STATUS_VIEW в виде «встроенного представления» в запрос - если хотите. Протестировано с Oracle 11c (см. dbfiddle ) и 18c.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...