Подтвердить что ты не робот

Выберите пользователей, принадлежащих только определенным отделам

У меня есть следующая таблица с двумя полями a и b, как показано ниже:

create table employe
(
    empID varchar(10),
    department varchar(10)
);

Вставка некоторых записей:

insert into employe values('A101','Z'),('A101','X'),('A101','Y'),('A102','Z'),('A102','X'),
             ('A103','Z'),('A103','Y'),('A104','X'),('A104','Y'),('A105','Z'),('A106','X');


select * from employe;
empID   department
------------------
A101    Z
A101    X
A101    Y
A102    Z
A102    X
A103    Z
A103    Y
A104    X
A104    Y
A105    Z
A106    X

Примечание. Теперь я хочу показать сотрудника, который принадлежит только отделу Z и Y.   Поэтому в соответствии с условием должен отображаться только сотрудник A103, потому что он принадлежит только   в отдел Z и Y. Но сотрудник A101 не должен появляться, потому что он принадлежит Z,X, and Y.

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

Если условие равно: Z и Y, тогда результат должен быть:

empID
------
A103

Если условие равно: Z и X, тогда результат должен быть:

empID
------
A102

Если условие: Z, X и Y, тогда результат должен быть:

empID
------
A101

Примечание. Я хочу сделать это только в предложении where (не хочу использовать предложения group by и having), потому что я собираюсь включить это один в другом where.

4b9b3361

Ответ 1

Это Реляционный раздел без проблемы останова (RDNR). См. статью Dwain Camps, которая предоставляет множество решений для этой проблемы.

Первое решение

SQL Fiddle

SELECT empId
FROM (
    SELECT
        empID, cc = COUNT(DISTINCT department)
    FROM employe
    WHERE department IN('Y', 'Z')
    GROUP BY empID
)t
WHERE
    t.cc = 2
    AND t.cc = (
        SELECT COUNT(*)
        FROM employe
        WHERE empID = t.empID
    )

Второе решение

SQL Fiddle

SELECT e.empId
FROM employe e
WHERE e.department IN('Y', 'Z')
GROUP BY e.empID
HAVING
    COUNT(e.department) = 2
    AND COUNT(e.department) = (SELECT COUNT(*) FROM employe WHERE empID = e.empId)

Без использования GROUP BY и HAVING:

SELECT DISTINCT e.empID
FROM employe e
WHERE
    EXISTS(
        SELECT 1 FROM employe WHERE department = 'Z' AND empID = e.empID
    )
    AND EXISTS(     
        SELECT 1 FROM employe WHERE department = 'Y' AND empID = e.empID
    )
    AND NOT EXISTS(
        SELECT 1 FROM employe WHERE department NOT IN('Y', 'Z') AND empID = e.empID
    )

Ответ 2

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

Примечание: Значения должны быть перечислены в алфавитном порядке.

XML-решение с помощью CROSS APPLY

select DISTINCT empID
FROM employe A
CROSS APPLY
            (
                SELECT department + ','
                FROM employe B
                WHERE A.empID = B.empID
                ORDER BY department
                FOR XML PATH ('')
            ) CA(Deps)
WHERE deps = 'Y,Z,'

Результаты:

empID
----------
A103

Ответ 3

Для условия 1: z и y

 select z.empID from (select empID from employe where department = 'z' ) as z
inner join (select empID from employe where department = 'y' )  as y 
on z.empID = y.empID
where z.empID Not in(select empID from employe where department = 'x' ) 

Для условия 1: z и x

select z.empID from (select empID from employe where department = 'z' ) as z
inner join (select empID from employe where department = 'x' )  as x 
on z.empID = x.empID
where z.empID Not in(select empID from employe where department = 'y' )

Для условия 1: z, y и x

select z.empID from (select empID from employe where department = 'z' ) as z
inner join (select empID from employe where department = 'x' )  as x 
on z.empID = x.empID
inner join (select empID from employe where department = 'y' )  as y on 
y.empID=Z.empID

Ответ 4

Вы можете использовать GROUP BY с having следующим образом. SQL Fiddle

SELECT empID 
FROM employe
GROUP BY empID
HAVING SUM(CASE WHEN department= 'Y' THEN 1 ELSE 0 END) > 0
AND SUM(CASE WHEN department= 'Z' THEN 1 ELSE 0 END) > 0
AND SUM(CASE WHEN department NOT IN('Y','Z') THEN 1 ELSE 0 END) = 0

Без GROUP BY и having

SELECT empID 
FROM employe E1
WHERE (SELECT COUNT(DISTINCT department) FROM employe E2 WHERE E2.empid = E1.empid and  department IN ('Z','Y')) = 2
EXCEPT
SELECT empID 
FROM employe
WHERE department NOT IN ('Z','Y')

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

;WITH CTE AS 
(

    SELECT empID 
    FROM employe
    GROUP BY empID
    HAVING SUM(CASE WHEN department= 'Y' THEN 1 ELSE 0 END) > 0
    AND SUM(CASE WHEN department= 'Z' THEN 1 ELSE 0 END) > 0
    AND SUM(CASE WHEN department NOT IN('Y','Z') THEN 1 ELSE 0 END) = 0
)
SELECT cols from CTE join othertable on col_cte = col_othertable

Ответ 5

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

select empID from employe 
where empId in (select empId from employe 
where department = 'Z' and department = 'Y') 
and empId not in (select empId from employe 
where department = 'X') ;

Ответ 6

для условия If: Z и Y

   SELECT EMPID FROM EMPLOYE WHERE DEPARTMENT='Z'  AND 
   EMPID IN (SELECT EMPID FROM EMPLOYE WHERE DEPARTMENT ='Y')AND
   EMPID NOT IN(SELECT EMPID FROM EMPLOYE WHERE DEPARTMENT NOT IN ('Z','Y'))

Ответ 7

Следующий запрос работает, когда вы хотите, чтобы сотрудники из отделов "Y" и "Z", а не "X".

select empId from employe 
where empId in (select empId from employe 
                where department = 'Z') 
and empId in (select empId from employe 
              where department = 'Y') 
and empId not in (select empId from employe 
                  where department = 'X') ;

Для вашего второго случая просто замените not in на in в последнем условии.

Ответ 8

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

SELECT  a.empId
FROM    employe a
        INNER JOIN
        (
            SELECT  empId
            FROM    employe 
            WHERE   department IN ('X', 'Y', 'Z')
            GROUP   BY empId
            HAVING  COUNT(*) = 3
           )b ON a.empId = b.empId
GROUP BY a.empId

Счет должен основываться на количестве условий.

Ответ 9

Вы также можете использовать GROUP BY и HAVING — вам просто нужно сделать это в подзапросе.

Например, начните с простого запроса, чтобы найти всех сотрудников в отделах X и Y (а не в каких-либо других отделах):

SELECT empID,
  GROUP_CONCAT(DISTINCT department ORDER BY department ASC) AS depts
FROM emp_dept GROUP BY empID
HAVING depts = 'X,Y'

Я использовал MySQL GROUP_CONCAT() как удобный ярлык здесь, но вы можете получить те же результаты без него, например, например:

SELECT empID,
  COUNT(DISTINCT department) AS all_depts,
  COUNT(DISTINCT CASE
    WHEN department IN ('X', 'Y') THEN department ELSE NULL
  END) AS wanted_depts
FROM emp_dept GROUP BY empID
HAVING all_depts = wanted_depts AND wanted_depts = 2

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

SELECT empID, name, depts
FROM employees
JOIN (
    SELECT empID,
      GROUP_CONCAT(DISTINCT department ORDER BY department ASC) AS depts
    FROM emp_dept GROUP BY empID
    HAVING depts = 'X,Y'
  ) AS tmp USING (empID)
WHERE -- ...add other conditions here...

Здесь SQLFiddle демонстрирует этот запрос.


Ps. Причина, по которой вам следует использовать JOIN вместо подзапроса IN для этого, заключается в том, что MySQL не так хорош в оптимизации подзапросов IN.

В частности (по крайней мере, по версии 5.5) MySQL всегда преобразует подзапросы IN в зависимые подзапросы, так что подзапрос должен быть повторно выполнен для каждой строки внешнего запроса, даже если исходный подзапрос был независимым, Например, следующий запрос (из приведенной выше документации):

SELECT ... FROM t1 WHERE t1.a IN (SELECT b FROM t2);

эффективно преобразуется в:

SELECT ... FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE t2.b = t1.a);

Это может быть достаточно быстро, если t2 мал и/или имеет индекс, позволяющий быстро искать. Однако, если (например, в исходном примере выше) выполнение подзапроса может занять много работы, производительность может сильно пострадать. Использование JOIN вместо этого позволяет подзапросу выполнять только один раз и, как правило, обеспечивает гораздо лучшую производительность.

Ответ 10

Как насчет самостоятельного присоединения? (ANSI Compliant - работал более 20 лет)

SELECT * FROM employee e JOIN employee e2 ON e.empid = e2.empid
WHERE e.department = 'x' AND e2.department ='y'

Это показывает, что a101 и a104 работают в обоих отделах.

Ответ 11

Решение с использованием предложения where:

select distinct e.empID
from employe e
where exists( select * 
              from employe
              where empID = e.empID
              having count(department) = count(case when department in('Y','X','Z') then department end)
                 and count(distinct department) = 3)

exists проверяет, имеются ли записи для определенного EmpId, которые имеют общий счет department, равный условному счету только соответствия department и что он также равен числу department, предоставленному in. Также стоит упомянуть, что здесь мы применяем предложение having без предложения group by во всем наборе, но с уже указанным, только одним EmpId.

SQLFiddle

Вы можете добиться этого без коррелированного подзапроса, но с предложением group by:

select e.empId
from employe e
group by e.empID
having count(department) = count(case when department in('Y','X','Z') then department end)
   and count(distinct department) = 3

SQLFiddle

Вы также можете использовать другой вариант предложения having для запроса выше:

having count(case when department not in('Y','X', 'Z') then department end) = 0
   and count(distinct case when department in('Y','X','Z') then department end) = 3

SQLFiddle

Ответ 12

В Postgres это можно упростить с помощью массивов:

select empid
from employee
group by empid
having array_agg(department order by department)::text[] = array['Y','Z'];

Важно сортировать элементы в array_agg() и сравнивать их с отсортированным списком отделов в том же порядке. В противном случае это не вернет правильные ответы.

например. array_agg(department) = array['Z', 'Y'] может привести к неправильным результатам.

Это можно сделать более гибким способом, используя CTE для обеспечения отделов:

with depts_to_check (dept) as (
   values ('Z'), ('Y')
)
select empid
from employee
group by empid
having array_agg(department order by department) = array(select dept from depts_to_check order by dept);

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


Параметр со стандартным SQL должен проверить, имеет ли по меньшей мере одну строку другой раздел вместе с подсчетом всех строк

select empid
from employee
group by empid
having min(case when department in ('Y','Z') then 1 else 0 end) = 1
  and count(case when department in ('Y','Z') then 1 end) = 2;

Вышеупомянутое решение не будет работать, если возможно, что один сотрудник назначается дважды в тот же отдел!

having min (...) можно упростить в Postgres, используя агрегат bool_and().

При применении стандартного условия filter() для выполнения условной агрегации это также может быть сделано для работы с ситуацией, когда работник может быть назначен на тот же отдел дважды

select empid
from employee
group by empid
having bool_and(department in ('Y','Z'))
  and count(distinct department) filter (where department in ('Y','Z')) = 2;

bool_and(department in ('Y','Z')) возвращает true, если условие истинно для строк all в группе.


Другим решением со стандартным SQL является использование пересечения между теми сотрудниками, которые имеют по крайней мере эти два отдела и те, которые назначены ровно в два отдела:

-- employees with at least those two departments
select empid
from employee
where department in name in ('Y','Z')
group by empid
having count(distinct department) = 2

intersect

-- employees with exactly two departments
select empid
from employee
group by empid
having count(distinct department) = 2;