I have the following table with two fields namely a and b as shown below:
create table employe
(
empID varchar(10),
department varchar(10)
);
I know that this question has already been answered, but it was a fun problem to do and I tried to do it in a way that no one else has. Benefits of mine is that you can input any list of strings as long as each value has a comma afterwards and you don't have to worry about checking counts.
Note: Values must be listed in alphabetic order.
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,'
Results:
empID
----------
A103
This is a Relational Division with no Remainder (RDNR) problem. See this article by Dwain Camps that provides many solution to this kind of problem.
First Solution
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
)
Second Solution
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)
Without using GROUP BY and 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
)
What about a self join? (ANSI Compliant - worked for 20+ years)
SELECT * FROM employee e JOIN employee e2 ON e.empid = e2.empid
WHERE e.department = 'x' AND e2.department ='y'
This shows that a101 and a104 both work in both departments.
Solution using where clause:
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 checks if there are records for specific EmpId that have total count of departments equal to conditional count of only matching departments and that it is also equal to the number of departments provided to the in clause. Also worth mentioning that here we apply having clause without the group by clause, on the whole set, but with already specified, only one empID.
SQLFiddle
You can achieve this without the correlated subquery, but with the group by clause:
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
You can also use another variation of having clause for the query above:
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
try this
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') ;
You can use GROUP BY with having like this. 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
Without GROUP BY and 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')
If you want to use any of the above query with other tables using a join you can use CTE or a derived table like this.
;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