使用IN和NOT IN条件在单个表上进行内部查询

时间:2012-08-29 00:34:59

标签: sql oracle relational-division

这是我之前answered question

的修改

我的数据如下表所示:

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

我想获得只有13和15的用户ID。所以根据上面的例子,我应该只回来USER D

下面的查询是在我之前的回答中提供的,然而我添加了NOT IN部分,但是没有达到目标..

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

5 个答案:

答案 0 :(得分:4)

要仅 13和15,请执行以下操作:

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

这将检查13和15是否在user_id集中。然后检查集合中没有其他内容。

我意识到使用带有case语句的having子句起初看起来很尴尬。但是,您可以表达关于集合中不同事物组合的大量逻辑。

答案 1 :(得分:2)

假设user_idrole_id的组合是唯一的,您可以执行类似

的操作
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

如果user_idrole_id的组合不是唯一的,那么原始distinct中的count是必要的,事情会变得更具挑战性。

答案 2 :(得分:0)

  

我希望role_id为13和15(没有别的)的用户

一种方法:

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;

这假定(role_id, user_id)是唯一的。

这是关系师的一个特例。我们最近已经汇集了很多方法来解决这个问题(对于PostgreSQL和MySQL,但大部分都是基本的SQL,也适用于Oracle):
How to filter SQL results in a has-many-through relation

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)
   );

答案 3 :(得分:0)

实际上没有测试过,但这样的事情应该有效:

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

用简单的英语:忽略任何包含不良ROLE_ID(NOT EXISTS)的行的用户行,然后继续确保用户拥有所有理想的ROLE_ID。

注意:如果{ROLE_ID,USER_ID}上有密钥,则无需DISTINCT。


要让那些只有(13,15)且没有其他内容的用户事先知道什么是“其他”,只需将内部查询中的ROLE_ID IN (11, 14)替换为ROLE_ID NOT IN (13, 15)

答案 4 :(得分:0)

answer扩展到原始问题,您可以尝试

(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)

INTERSECTMINUS都特定于ORACLE,因此其他解决方案更通用,而且更易读,更容易修改。

无法访问ORACLE数据库,因此未经过测试。