#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.