Возникла проблема в PostgreSQL Query - PullRequest
0 голосов
/ 28 мая 2018

У меня есть следующие таблицы:

create table employee(id int, name varchar(50), fname varchar(50));

create table rank (id int, name varchar(50));

create table promotion(id int, dt date, from_rank_id int, to_rank_id int, employee_id int,
constraint fk_pro_emp foreign key(employee_id) references employee(id),
constraint fk_pro_rank_f foreign key(from_rank_id) references rank(id),
constraint fk_pro_rank_t foreign key(to_rank_id) references rank(id));

insert into employee values(1, 'John', 'Roy'), (2, 'Kane', 'Williamson'), (3, 'Yasin', 'Khan'), (4, 'Dwayne', 'Brain');

insert into rank values(1, 'A'), (2, 'B'), (3, 'C'), (4, 'D'), (5, 'E');

--One person can have many promotions so I insert two records for the same person
insert into promotion values(1, '2010-01-01', 1, 2, 1), (2, '2015-01-01', 2, 3, 1);

--I insert three promotions for the second employee
insert into promotion values(4, '2011-11-23', 1, 2, 2), (5, '2012-04-05', 2, 3, 2), (6, '2013-12-30', 3, 4, 2);

--I insert one record for the third person
insert into promotion values(7, '2015-10-21', 3, 4, 3);

--The last person does not have promotion  

Теперь я хочу выбрать записи сотрудника вместе с их последними (максимальная дата продвижения по службе) записями продвижения по службе.
Требуемый результат:

EMP_ID      Name      Father_Name     Pro_Date    From_Rank    To_Rank  
   1        John         Roy          2015-01-01     B            C  
   2        Kane       Williamson     2013-12-30     C            D  
   3        Yasin        Khan         2015-10-21     C            D  
   4        Dwayne       Brain         <Null>       <Null>      <Null>   

Заранее благодарим за любую помощь.

Ответы [ 3 ]

0 голосов
/ 28 мая 2018

Обычный способ решения проблем в Postgres - это использование (проприетарного) оператора distinct on().

Следующий запрос возвращает последнее повышение по службе для каждого сотрудника:

select distinct on (p.employee_id) p.employee_id
from promotion p
order by p.employee_id, p.dt desc

Это можно объединить с таблицей rank, чтобы получить ранговые имена:

select distinct on (p.employee_id) p.employee_id, p.dt as promotion_date, fr.name as from_rank, tr.name as to_rank
from promotion p
  join rank tr on tr.id = p.to_rank_id
  join rank fr on fr.id = p.from_rank_id
order by p.employee_id, p.dt desc

Ваша таблица настройки позволяет вставлять рекламные акции без from_rank_id или to_rank_id, потому что эти столбцы обнуляются.Если это действительно разрешено (в чем я сомневаюсь), вам нужно изменить эти соединения на внешние соединения.По крайней мере, to_rank_id должен быть определен как NOT NULL, но, вероятно, оба.

Этот запрос в свою очередь может быть присоединен к таблице employee:

select e.*, t.*
from employee e 
  left join (
     select distinct on (p.employee_id) p.employee_id, p.dt as promotion_date, fr.name as from_rank, tr.name as to_rank
     from promotion p
       join rank tr on tr.id = p.to_rank_id
       join rank fr on fr.id = p.from_rank_id
     order by p.employee_id, p.dt desc
  ) t on t.employee_id = e.id
order by e.id;

Онлайн пример: http://rextester.com/NWBDFL38394

0 голосов
/ 28 мая 2018

Попробуйте это -

SELECT e.id, e.name, e.fname, p3.dt pro_date, p3.name2 From_Rank, p3.name1 To_Rank
FROM employee e
left join (select p.dt dt, p.employee_id, r.name name1, r2.name name2
           from promotion p
           inner join (select max(dt) dt, employee_id
                       from promotion
                       group by employee_id) p2
                       on p.dt = p2.dt
          left join rank r on p.to_rank_id = r.id
          left join rank r2 on p.from_rank_id = r2.id) p3 
on e.id = p3.employee_id

http://www.sqlfiddle.com/#!17/bd7e4/50

0 голосов
/ 28 мая 2018

Вы можете использовать объединение LATERAL и подзапрос, выбирая повышение с наибольшим значением dt для данного employee_id, используя ORDER BY dt DESC LIMIT 1, чтобы получить сотрудников, которые были повышены один раз, вместе с данными повышения.UNION ALL сотрудников, которые никогда не повышались, что вы можете проверить с помощью NOT EXISTS (SELECT * FROM promotion ...).

SELECT e.id "EMP_ID",
       e.name "Name",
       e.fname "Father_Name",
       x.dt "Pro_Date",
       x.rank_before "From_Rank",
       x.rank_after "To_Rank"
       FROM employee e
            CROSS JOIN LATERAL (SELECT p.dt,
                                       rb.name rank_before,
                                       ra.name rank_after
                                       FROM promotion p
                                            LEFT JOIN rank rb
                                                      ON rb.id = p.from_rank_id
                                            LEFT JOIN rank ra
                                                      ON ra.id = p.to_rank_id
                                       WHERE p.employee_id = e.id
                                       ORDER BY p.dt DESC
                                       LIMIT 1) x
UNION ALL
SELECT e.id "EMP_ID",
       e.name "Name",
       e.fname "Father_Name",
       NULL "Pro_Date",
       NULL "From_Rank",
       NULL "To_Rank"
       FROM employee e
       WHERE NOT EXISTS (SELECT *
                                FROM promotion p
                                WHERE p.employee_id = e.id);
...