HAVING子句中的逻辑,用于通过结果获取组的多个值

时间:2012-08-29 02:34:41

标签: sql oracle

想象一下,我的数据表格如下:

ROLE_ID | USER_ID  |  CODE
---------------------------------
 14     | USER A   |   001
 15     | USER A   |   002
 11     | USER B   |   004
 13     | USER D   |   005
 13     | USER A   |   001
 15     | USER B   |   009
 15     | USER D   |   005
 12     | USER C   |   004
 15     | USER C   |   008
 13     | USER D   |   007
 15     | USER D   |   007

我想获得只有13和15个role_ids的用户ID和代码。所以基于上面的数据,我想回到以下

USER D |  005
USER D |  007

我有下面的查询,但是,它只会带回一个,而不是两个。

   SELECT a.user_id, a.code
   FROM my_table a
   WHERE a.ROLE_ID in (13,15,11,14)
   group by a.USER_ID, a.code
    having sum( case when a.role_id in (13,15) then 1 else 0 end) = 2
    and sum( case when a.role_id in (11,14) then 1 else 0 end) = 0  
   ORDER BY USER_ID

以上查询仅带来

USER D |  005

而不是

USER D |  005
USER D |  007

4 个答案:

答案 0 :(得分:2)

有时只用英语听你自己的单词会转化为最容易阅读的SQL:

SELECT DISTINCT a.user_id, a.code
   FROM my_table a
   WHERE a.user_id in 
       (SELECT b.user_id
       FROM my_table b
       WHERE b.ROLE_ID = 13)
    AND a.user_id in 
       (SELECT b.user_id
       FROM my_table b
       WHERE b.ROLE_ID = 15)
   AND a.user_id NOT IN 
       (SELECT b.user_id
       FROM my_table b
       WHERE b.ROLE_ID NOT IN (13,15))

答案 1 :(得分:0)

我会:

SELECT a.user_id, a.code 
FROM my_table a
GROUP BY a.user_id, a.code 
HAVING sum(case when a.role_id in (13, 15) then 1 else 3 end) = 2

:)

答案 2 :(得分:0)

SELECT user_id, code FROM my_table
WHERE role_id = 13
INTERSECT
SELECT user_id, code FROM my_table
WHERE role_id = 15

答案 3 :(得分:0)

EthanB的证明,您的查询完全按照您的要求运行。您的项目数据中一定有某些内容不在问题的伪造数据中。

我确实支持您在问题中执行的枢轴,但是我将其编写为单个SUM表达式,以减少汇总数据上的迭代次数。我当然不赞成multiple subqueries on each row of the table123)...无论优化程序是否将子查询转换为多个JOIN。

您的关键条件:

having sum( case when a.role_id in (13,15) then 1 else 0 end) = 2
   and sum( case when a.role_id in (11,14) then 1 else 0 end) = 0 

我的推荐:

在对汇总数据进行迭代时,您可以保持合格行的计数(+1),并在每次评估后跳转到不合格结果(+3)。这样,聚合上只有一次通过而不是两次。

SELECT USER_ID, CODE
FROM my_table
WHERE ROLE_ID IN (13,15,11,14)
GROUP BY USER_ID, CODE
HAVING SUM(CASE WHEN ROLE_ID IN (13,15) THEN 1
                WHEN ROLE_ID IN (11,14) THEN 3 END) = 2

表达这些HAVING子句正在做什么的另一种方法是:

要求第一个CASE满足两次,第二个CASE从不满足。

Demo Link

或者,上面的HAVING子句可以写得不太优雅:

HAVING SUM(CASE ROLE_ID
           WHEN 13 THEN 1
           WHEN 15 THEN 1
           WHEN 11 THEN 3
           WHEN 14 THEN 3
           END) = 2

免责声明#1::我不会在[oracle]标签池中游泳,也没有研究如何使用PIVOT执行此操作。

免责声明#2:我的上述建议假设ROLE_ID在分组的USER_ID + CODE聚合数据中是唯一的。附带情况:(a demo

  1. 给定的组包含ROLE_ID = 13ROLE_ID = 13ROLE_ID = 15,那么SUM当然至少为3,并且该组不符合资格。
  2. 给定的组仅包含ROLE_ID = 15ROLE_ID = 15,那么SUM当然为2,并且该组将被无意地限制。

要应对此类情况,请设置三个单独的MAX条件。

HAVING MAX(CASE WHEN ROLE_ID = 13 THEN 1 END) = 1
   AND MAX(CASE WHEN ROLE_ID = 15 THEN 1 END) = 1
   AND MAX(CASE WHEN ROLE_ID IN (11,14) THEN 1 END) IS NULL

Demo