问题
This is a modification to my previously answered question
I have data in the table like below:
ROLE_ID | USER_ID
------------------
14 | USER A
15 | USER A
11 | USER B
13 | USER D
13 | USER A
15 | USER B
15 | USER D
12 | USER C
15 | USER C
I would like to get user ids that ONLY have 13 and 15. So based on the example above, I should only get back USER D
The query below was provided in my previous answer and the NOT IN
part was added by me, however, that doesn't achieve the goal..
select user_id
from my_table
where role_id in (13,15) AND role_id not in (11,14)
group by user_id.
having count(distinct role_id) = 2
回答1:
Assuming the combination of user_id
and role_id
is unique, you could do something like
select user_id
from my_table
where role_id in (13,15,11,14)
group by user_id.
having sum( case when role_id in (13,15) then 1 else 0 end) = 2
and sum( case when role_id in (11,14) then 1 else 0 end) = 0
If the combination of user_id
and role_id
is not unique, then the distinct
in your original count
is necessary and things get a bit more challenging.
回答2:
To get only 13 and 15, do the following:
select user_id
from my_table
group by user_id
having max(case when role_id = 13 then 1 else 0 end) = 1 and -- has 13
max(case when role_id = 15 then 1 else 0 end) = 1 and -- has 15
max(case when role_id not in (13, 15) then 1 else 0 end) = 0 -- nothing else
This checks that 13 and 15 are in the user_id set. It then checks that nothing else is in the set.
I realize that using the having clause with the case statement seems awkward at first. However, you can express lots of logic about different combinations of things in the set.
回答3:
I want the users that have role_id of 13 AND 15 (and nothing else)
One way to do it:
SELECT t13.user_id
FROM my_table t13
JOIN my_table t15 ON t13.user_id = t15.user_id
LEFT JOIN my_table tx ON tx.user_id = t13.user_id AND tx.role_id NOT IN (13,15)
WHERE t13.role_id = 13
AND t15.role_id = 15
AND tx.user_id IS NULL;
This assumes (role_id, user_id)
to be unique.
It's a special case of relational division. We have assembled a ton of ways to deal with that recently (for PostgreSQL and MySQL, but most of it is basic SQL that works for Oracle just as well):
How to filter SQL results in a has-many-through relation
Another variant with NOT EXISTS:
SELECT t13.user_id
FROM my_table t13
JOIN my_table t15 ON t13.user_id = t15.user_id
WHERE t13.role_id = 13
AND t15.role_id = 15
AND NOT EXISTS (
SELECT 1
FROM my_table tx
WHERE tx.user_id = t13.user_id
AND tx.role_id NOT IN (13,15)
);
回答4:
Didn't actually test it, but something like this should work:
SELECT USER_ID
FROM MY_TABLE T1
WHERE
ROLE_ID IN (13, 15)
AND NOT EXISTS (
SELECT *
FROM MY_TABLE T2
WHERE
T1.USER_ID = T2.USER_ID
AND ROLE_ID IN (11, 14)
)
GROUP BY USER_ID
HAVING COUNT(DISTINCT ROLE_ID) = 2
In plain English: ignore all rows of a user for which there is any row containing undesirable ROLE_ID (NOT EXISTS
), then proceed to ensure user has all of the desirable ROLE_IDs.
NOTE: DISTINCT is not necessary if there is a key on {ROLE_ID, USER_ID}.
To get users that have only (13, 15), and nothing else, without knowing in advance what is "else", just replace the ROLE_ID IN (11, 14)
in the inner query with ROLE_ID NOT IN (13, 15)
.
回答5:
Extending my answer to your original question, you might try
(SELECT user_id FROM table WHERE role_id=13
INTERSECT
SELECT user_id FROM table WHERE role_id=15)
MINUS
SELECT user_id FROM table WHERE role_id IN (11, 14)
Both INTERSECT
and MINUS
are specific to ORACLE, so the other solutions are more generic, whereas this is more readable and easier to modify.
Don't have access to an ORACLE db so not tested.
来源:https://stackoverflow.com/questions/12169409/inner-queries-on-a-single-table-with-in-and-not-in-conditions