SQL вопрос — Как получить сотрудников с теми же навыками, что и у конкретного сотрудника

#sql #oracle #count #relational-division

#sql #Oracle #количество #реляционное разделение

Вопрос:

У меня есть таблица, вызываемая Employee со столбцами:

  • EMPID INT (первичный ключ)
  • EMPNAME VARCHAR2(100)

С примерами данных следующим образом:

 empid|empname|
-----|-------|
    1|Mary   |
    2|Bob    |
  

У меня есть другая таблица Employee_Skills со столбцами:

  • EMPID INT
  • ПАРАМЕТР НАВЫКА2(100)

Пример которого может быть представлен следующим образом:

 empid|skill |
-----|------|
    1|SKILL |
    1|Java  |
    1|C     |
    1|Python|
    2|C     |
    2|Python|
  

Во 2-й таблице оба столбца вместе образуют первичный ключ. И EMPID имеет ссылочное ограничение, указывающее на Employee(EMPID) .

Я хочу получить всех сотрудников, обладающих всеми навыками, которые есть у Мэри.

Например,

 select t2.skill 
from Employee t1 
join Employee_Skills t2 on (t1.EMPID = t2.EMPID) 
where t1.EMPNAME = 'Mary'
  

Этот запрос вернул эти строки:

 SKILL
------------
Java
C  
Python
... (a few more)
  

Теперь я хочу выбрать всех сотрудников, обладающих вышеуказанными навыками (могут иметь больше навыков, но должны иметь хотя бы те, которые есть у Мэри).

Я предпринял несколько попыток, но ничто не дает мне именно то, что мне нужно.

Вот одна попытка, которая не сработала:

 select t1.EMPID, t1.EMPNAME
from Employees t1 
join Employee_Skills t2 on (t1.EMPID = t2.EMPID)
where t2.SKILL = ALL (select t4.skill 
                      from Employee t3  
                      join Employee_Skills t4 on (t3.EMPID = t4.EMPID) 
                      where t3.EMPNAME = 'Mary');
  

Комментарии:

1. Пожалуйста, предоставьте репрезентативные данные и укажите конкретную СУБД, которую вы используете (Oracle, Postgres, DB2, Microsoft SQL Server)

Ответ №1:

Вы можете решить эту проблему с разделением отношений с помощью объединений и агрегирования:

 select e2.empid
from employee e1
inner join employee_skills es1 on es1.empid = e1.empid
inner join employee_skills es2 on es2.skill = es1.skill and es2.empid <> es1.empid 
inner join employee e2 on e2.empid = es2.empid
where e1.empname = 'Mary'
group by e2.empid
having count(*) = (
    select count(*)
    from employee e3
    inner join employee_skills es3 on es3.empid = e3.empid
    where e3.empname = 'Mary'
)
  

Ответ №2:

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

 select emp.empid, emp.empname
from Employee_Skills esk
inner join employee emp
 on emp.empid = esk.empid
where emp.empname <> 'Mary' 
 and exists (select null   
                from employee_skills esk_
                 inner join employee emp_
                  on emp_.empid = esk_.empid
                where emp_.empname = 'Mary'
                 and esk_.skill = esk.skill);
  

PS: Я исключил Мэри из возвращенного списка.

Я воссоздал ваши данные следующим образом:

 create table employee(EMPID INT , EMPNAME VARCHAR(100));
                                                   
create table Employee_Skills(EMPID INT, SKILL VARCHAR(100));
                                                       
insert into employee values (1, 'Mary');
insert into employee values (2, 'Bob');
                                                       
insert into employee_skills values (2, 'SKILL');
insert into employee_skills values (2, 'Java');
insert into employee_skills values (1, 'C  ');
insert into employee_skills values (1, 'Python');                                 
insert into employee_skills values (2, 'C  ');
insert into employee_skills values (2, 'Python');          
                
  

Комментарии:

1. Спасибо, Ларс. В приведенном вами примере у Мэри 5 навыков. У Боба их всего 2. Таким образом, Боб не должен отображаться в конечном результате.

2. Понятно. Я пропустил эту часть требований. Я соответствующим образом обновил свой ответ.

Ответ №3:

Это сработало. Совместное использование для всех. По сути, вы берете навыки Мэри. Сделайте МИНУС навыков текущего сотрудника. Если вы получаете пробел, вы показываете этого сотрудника в результате.

 Select t4.empid, t4.empname from employee t4
Where not exists 
(Select t1.skill from employee_skills t1 join employee t2 on (t1.empid = t2.empid) where t2.empname = 'Mary'
Minus
Select t3.skill from employee_skills t3 where t3.empid = t4.empid)
and t4.empname <> 'Mary';

  

Комментарии:

1. Это специфично для Oracle. Я пометил ваш вопрос Oracle (из-за типа Varchar2), и minus то, как вы демонстрируете здесь, является еще одной функцией Oracle.