【问题标题】:Oracle SQL - is there a standard HAVING EVERY workaround?Oracle SQL - 是否有一个标准的 HAVING EVERY 解决方法?
【发布时间】:2013-02-21 21:13:03
【问题描述】:

我无法找到解决 Oracle 不支持 HAVING EVERY 子句的方法。

我有两个表,Production 和 Movie,具有以下架构:

Production (pid, mid)
Movie(mid, director)

其中 'pid' 是表示发行商 ID 的整数,'mid' 是表示电影 ID 的整数,director 是电影导演的姓名。

我的目标是获取仅出版过彼得·杰克逊或本·阿弗莱克导演的电影的出版商列表(按 ID)。

为了实现这一点,我编写了以下查询:

SELECT *
    FROM Production P, Movie M
    WHERE P.mid = M.mid;
    GROUP BY P.pid
    HAVING EVERY ( M.director IN ('Ben Affleck', 'Peter Jackson') );

但由于 Oracle 不支持 HAVING EVERY,我得到的只是以下错误:

    HAVING EVERY ( M.director IN ('ben affleck', 'PJ') )
                          *
ERROR at line 5:
ORA-00907: missing right parenthesis

由于导演职位必须适用于出版商制作的每一部电影,我不认为可以将条件移至 WHERE 子句。

有没有办法绕过这个障碍?任何被认为是“标准”的东西?此外(也许更重要的是)为什么 Oracle 选择不实施 HAVING EVERY?

【问题讨论】:

    标签: sql oracle sqlplus having-clause


    【解决方案1】:

    试试这个:

    SELECT P.pid
    FROM (select distinct Pi.pid, M.Director
          from Production Pi INNER JOIN 
        Movie M ON Pi.mid = M.mid) P
    GROUP BY P.pid
    HAVING sum(case when P.Director in ('Ben Affleck', 'Peter Jackson') 
               then 1 else 99 end) = 2
    

    Here is a sqlfiddle demo

    【讨论】:

    • 这似乎可以解决问题!太感谢了。当我们在这里时,您知道为什么 Oracle 没有实施 HAVING EVERY 吗?我觉得变通方法的代码非常笨拙/难以阅读,这对我来说是一个非常消极的方面。
    • +1 -- 由于误解了 OP 的要求,我删除了答案。不错的答案!
    • “虽然我们在这里,你知道为什么 Oracle 没有实现 HAVING EVERY 吗?”。总的来说,当有商业动机时,Oracle 会添加特性。要么它使 Oracle 需要数据库为自己的应用程序做一些事情,要么它的客户有足够的需求(根据许可证支出的大小加权)。因此,虽然像我们这样的开发人员可能认为 HAVING EVERY 将是对 Oracle 工具箱的一个很好的补充,但任何重要的人都不太可能会注意 :)
    • @APC every 是 SQL 标准的一部分。无论如何,有一个解决方法。不过也太乏味了。使用min 方法:blog.jooq.org/2014/12/18/… 使用sum 方法:stackoverflow.com/questions/10407354/…
    • 这是一种避免幻数 (99) 的方法。 stackoverflow.com/questions/15012329/…
    【解决方案2】:

    经过一段时间的思考,我想出了一些可能比 A.B.Cade 想出的更具可读性的东西:

    select distinct P.pid
        from Production P
        where P.pid not in (
            -- Get publishers that have produced a movie directed by someone else
            select P1.pid
            from Production P1 INNER JOIN Movie M ON P1.mid = M.mid
            where M.director not in ('Ben Affleck', 'Peter Jackson')
        )
    

    SQLFiddle demo

    不同之处在于,我们不是只寻找具有所需导演的制片人,而是识别与其他导演相关的所有制片人,然后将其省略。

    【讨论】:

    • +1 跳出框框思考!唯一令人沮丧的是相关子查询,这在大型数据集上会变得昂贵。看我的回答。
    • 这将产生无效的结果。对于只有一位导演的出版商,它仍会显示该出版商。在此处查看如何真正模拟everystackoverflow.com/questions/15012329/…every(甚至模拟它)是不够的,需要使用 everycount
    【解决方案3】:

    基于Dan's own answer,但我已经删除了相关子查询,因为它在大型数据集上的性能可能会很差:

    SELECT DISTINCT P.pid
    FROM Production P
    LEFT JOIN (
        SELECT P1.pid
        FROM Production P1
        INNER JOIN Movie M ON (P1.mid = M.mid)
        WHERE M.director NOT IN ('Ben Affleck', 'Peter Jackson')
    ) V ON (P.pid = V.pid)
    WHERE v.pid IS NULL;
    

    SQL Fiddle demo

    【讨论】:

      【解决方案4】:

      为了避免magic number 99

      SELECT 
          P.pid
      FROM 
      (
          SELECT DISTINCT 
              Pi.pid, M.Director
          FROM Production Pi 
          JOIN Movie M ON Pi.mid = M.mid
      ) P
      GROUP BY 
          P.pid
      HAVING 
          COUNT(p.Director) = 2 -- The directors should be exactly 2
          AND MIN(CASE WHEN p.Director in ('Ben Affleck', 'Peter Jackson') 
                  THEN 1 ELSE 0 END) = 1
      

      其他方法:http://www.anicehumble.com/2019/04/not-every-rdbms-has-every.html

      请注意,不能使用双 not in 方法。因为它仍然会报告只有一位与本·阿弗莱克或彼得·杰克逊相匹配的导演的出版商。

      with production as
      (
         select *
         from (values 
            ('DC', 'Batman', 'Ben Affleck'),
            ('DC', 'Robin', 'Peter Jackson'),
            ('Not DC', 'Not Batman', 'Not Ben Affleck'),
            ('Not DC', 'Not Robin', 'Not Peter Jackson'),         
            ('Marvel', 'Avengers', 'Joe Russo'),
            ('WingNut', 'King Kong', 'Peter Jackson'),
            ('Century Fox', 'Deadpool', 'Ben Affleck'),
            ('Century Fox', 'Fantastic 4', 'Peter Jackson'),
            ('Century Fox', 'X-Men', 'Peter Jackson'),
            ('Millenium Fox', 'Scorpion', 'Ben Affleck'),
            ('Millenium Fox', 'Sub-Zero', 'Peter Jackson'),
            ('Millenium Fox', 'Liu Kang', 'Ed Boon')          
         ) as x(publisher, movie, director)
      )
      select distinct P.publisher
      from production P
      where P.publisher not in (
            -- Get publishers that have produced a movie directed by someone else
            select P1.publisher
            from production P1
            where P1.director not in ('Ben Affleck', 'Peter Jackson')
          )
      ;
      

      错误的输出。 WingNut 不应包括在内,因为它没有 Ben Affleck 和 Peter Jackson 作为导演。

      | publisher   |
      | ----------- |
      | WingNut     |
      | Century Fox |
      | DC          |
      

      这是正确的查询,每一个都是使用min(when true then 1 else 0) = 1模拟的

      with production as
      (
         select *
         from (values 
            ('DC', 'Batman', 'Ben Affleck'),
            ('DC', 'Robin', 'Peter Jackson'),
            ('Not DC', 'Not Batman', 'Not Ben Affleck'),
            ('Not DC', 'Not Robin', 'Not Peter Jackson'),         
            ('Marvel', 'Avengers', 'Joe Russo'),
            ('WingNut', 'King Kong', 'Peter Jackson'),
            ('Century Fox', 'Deadpool', 'Ben Affleck'),
            ('Century Fox', 'Fantastic 4', 'Peter Jackson'),
            ('Century Fox', 'X-Men', 'Peter Jackson'),
            ('Millenium Fox', 'Scorpion', 'Ben Affleck'),
            ('Millenium Fox', 'Sub-Zero', 'Peter Jackson'),
            ('Millenium Fox', 'Liu Kang', 'Ed Boon')          
         ) as x(publisher, movie, director)
      )
      select P.publisher
      from (select distinct publisher, director from production) P
      group by
          P.publisher
      having
           count(p.Director) = 2 -- The directors should be exactly 2
      
           and min(case when p.Director in ('Ben Affleck', 'Peter Jackson') 
                   then 1 else 0 end) = 1
      ;
      

      正确的输出,应该只显示 DC 和 Century Fox。因为他们是唯一一家专门雇佣 Ben Affleck 和 Peter Jackson 的出版商。

      | publisher   |
      | ----------- |
      | Century Fox |
      | DC          |
      

      现场测试:https://www.db-fiddle.com/f/aDDw4Pd1DJzs6J5HgbKbdh/4

      【讨论】:

        【解决方案5】:

        COUNT 使用条件聚合:

        SELECT Pi.pid
        FROM Production Pi
        JOIN Movie M ON Pi.mid = M.mid
        GROUP BY Pi.pid
        HAVING COUNT(DISTINCT CASE WHEN M.Director IN ('Ben Affleck','Peter Jackson') 
                                   THEN M.Director END) = 2;
        

        【讨论】:

          猜你喜欢
          • 2010-09-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-03-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-03-30
          相关资源
          最近更新 更多