【问题标题】:Inner queries on a single table with IN and NOT IN conditions具有 IN 和 NOT IN 条件的单个表的内部查询
【发布时间】:2012-08-29 00:34:58
【问题描述】:

这是对我之前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

【问题讨论】:

  • 您是否也希望拥有 role_id 13 15(仅此而已)的用户?
  • 我想要角色 ID 为 13 和 15 的用户(仅此而已)

标签: sql oracle relational-division


【解决方案1】:

只获得 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 子句一开始似乎很尴尬。但是,您可以表达关于集合中事物的不同组合的大量逻辑。

【讨论】:

    【解决方案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 的组合不是唯一的,那么原始count 中的distinct 是必要的,并且事情变得更具挑战性。

    【讨论】:

      【解决方案3】:

      我想要角色 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,但其中大部分是同样适用于 Oracle 的基本 SQL):
      How to filter SQL results in a has-many-through relation

      另一个不存在的变体:

      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】:

        实际上并没有测试它,但是这样的东西应该可以工作:

        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) 且没有其他内容的用户,并且事先不知道什么是“else”,只需将内部查询中的 ROLE_ID IN (11, 14) 替换为 ROLE_ID NOT IN (13, 15)

        【讨论】:

          【解决方案5】:

          将我的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 数据库,因此未经过测试。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2023-03-21
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-01-17
            相关资源
            最近更新 更多