Suppose the table is:
| employee_id | branch | role |
|---|---|---|
| 1 | A | admin |
| 2 | A | engineer |
| 3 | A | finance |
| 4 | B | admin |
| 5 | B | finance |
| 6 | C | engineer |
How can I find the departments that do not have all the roles?
In this example:
- Department A has all the roles.
- Department B does not have engineer role.
- Department C does not have admin and finance roles.
What would be the SQL query to get this result?
Ideally, the output should be
| branch | role |
|---|---|
| B | engineer |
| C | admin |
| C | finance |
CodePudding user response:
We can use a calendar table left anti join approach here:
SELECT b.branch, r.`role`
FROM (SELECT DISTINCT branch FROM yourTable) b
CROSS JOIN (SELECT DISTINCT `role` FROM yourTable) r
LEFT JOIN yourTable t
ON t.branch = b.branch AND
t.`role` = r.`role`
WHERE t.branch IS NULL
ORDER BY SELECT b.branch, r.`role`;
Note that role was a reserved keyword in MySQL versions earlier than 8 . If you are using an earlier version, you should avoid using role as a column name.
CodePudding user response:
We can use CROSS JOIN to build all possible combinations of branch and role and then NOT EXISTS to exclude those combinations that really exist in the table:
SELECT DISTINCT b.branch, r.roles
FROM (SELECT branch FROM yourtable) b
CROSS JOIN (SELECT roles FROM yourtable) r
WHERE NOT EXISTS (SELECT 1 FROM yourtable
WHERE branch = b.branch AND roles = r.roles)
ORDER BY b.branch, r.roles;
Note: The column "role" was renamed to "roles" in my query to prevent that "role" will be read as SQL key word.
