SQL выбрать разные элементы в одном столбце на основе количества результатов из другой таблицы - PullRequest
0 голосов
/ 09 июля 2020

У меня есть 3 таблицы

Employees        EmployeePossitionsOverYears      Position
- Id             - EmployeeId                     - Id
- Firstname      - Year                           - Name
- LastName       - PositionId

Мне нужно выбрать список сотрудников с их должностями.

Если должность сотрудника не меняется с годами (каждая строка таблицы EmployeePossitionsOverYears где EmployeeId = 1 PossitionId = 6) Мне нужно выбрать только название должности.

Но если должность сотрудника меняется (по крайней мере, один из PositionId не похож на другие, где EmployeeId = 3), мне нужно выбрать все должности имена с соответствующими годами для этого сотрудника в один столбец nvarchar.

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

Я пробовал использовать CASE в чем-то вроде

select
PositionText = 
CASE 
(select count(*) from 
(select Distinct EmployeeId, PositionId from EmployeePossitionsOverYears where EmployeeId = Employees.Id))

Но это не работает, потому что я все еще не могу выбрать, не присоединяясь к таблице EmployeePossitionsOverYears.

Как мне выбрать только 1 позицию name, если все PositionIds одинаковы для данного сотрудника, но выберите все названия должностей + их лет, если в одном столбце есть как минимум 2 разных PositionIds без дополнительных строк, тогда есть сотрудники?

Пример:

Сотрудники

Id  FirstName  LastName
1   First      Emp
2   Second     Worker
3   Third      Person

EmployeePossitionsOverYears

EmployeeId  Year    PositionId
1           2010    1
1           2011    1
1           2012    1
2           2010    1
2           2011    2
2           2012    3
3           2010    1
3           2011    2
3           2012    1

Позиция

Id  Name
1   Junior
2   Middle
3   Senior

Ожидаемый результат:

FirstName  LastName   position
- First     - Emp       - Junior
- Second    - Worker    - 2010_Junior, 2011_Middle, 2012_Senior 
- Third     - Person    - 2010_Junior, 2011_Middle, 2012_Junior

Ответы [ 2 ]

1 голос
/ 09 июля 2020

Я бы сделал это так.

with cte as(
    select
        EmployeePossitionsOverYears.*,
        Position.Name
    from EmployeePossitionsOverYears
    inner join Position on Position.Id = EmployeePossitionsOverYears.PositionId)

select
    FirstName,
    LastName,
    -- count the number of unique PositionId
    case when count(distinct PositionId) = 1
        -- There is no change in position.
        then max(Position.name)
        -- If there is a change in position, edit with the year.
        else replace((select concat([year] , '_' , Name) as [data()]
                   from cte
                   where EmployeeId = EmployeePossitionsOverYears.EmployeeId
                   order by [year]
                   for xml path ('')), ' ', ',') 
    end AS MemberNames
from EmployeePossitionsOverYears
inner join Employees on Employees.id = EmployeePossitionsOverYears.EmployeeId
inner join Position on Position.Id = EmployeePossitionsOverYears.PositionId
group by EmployeeId, FirstName, LastName

Если вы используете SQLServer 2017 или более поздней версии, вы можете писать умнее, используя функцию string_agg.

0 голосов
/ 09 июля 2020

Более простой способ express это:

select e.FirstName, e.LastName,
       (case when count(distinct p.name) = 1
             then max(p.name)
             else string_agg(concat(epy.year, '_', p.name), ', ' order by epy.year)
        end) as positions
from employees e join
     EmployeePossitionsOverYears epy
     on e.id = epy.EmployeeId join
     Position p
     on p.Id = epy.PositionId
group by e.id, e.FirstName, e.LastName;

Однако я ожидаю, что вы захотите объединить соседние годы с одной и той же позицией. Для этого оконные функции могут определять, когда меняются позиции:

select e.FirstName, e.LastName,
       (case when count(distinct p.name) = 1
             then max(p.name)
             else string_agg(case when prev_position is null or prev_position <> position then concat(epy.year, '_', p.name) end, ', ')
        end) as positions
from (select ep.*,
             lag(position) over (partition by id order by year) as prev_position_year
      from (select e.id, e.firstname, e.lastname, epy.name, p.name as position
            from employees e join
                 EmployeePossitionsOverYears epy
                 on e.id = epy.EmployeeId join
                 Position p
                 on p.Id = epy.PositionId
           ) ep
     ) ep
group by e.id, e.FirstName, e.LastName;
...