Oracle SQL - 有一个标准的每个变通方法吗?

时间:2013-02-21 21:13:03

标签: sql oracle sqlplus having-clause

我无法找到解决Oracle缺乏对HAVING EVERY条款的支持的方法。

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

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

其中'pid'是表示发布者ID的整数,'mid'是表示电影ID的整数,director是电影导演的名称。

我的目标是获取一份出版商名单(按ID),这些出版商只发行由Peter Jackson或Ben Affleck执导的电影。

为了达到这个目的,我编写了以下查询:

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?

5 个答案:

答案 0 :(得分:5)

试试这个:

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

答案 1 :(得分:3)

在考虑了一段时间之后,我想出了一些可能比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

不同之处在于,我们不是寻找只有所需董事的生产者,而是确定与其他董事相关联的所有生产者,然后省略它们。

答案 2 :(得分:2)

基于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

答案 3 :(得分:0)

要避免使用magic number 99

for /?

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

请注意,不能使用双重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 方法。由于仍会报告只有一位与本·阿弗莱克或彼得·杰克逊相匹配的导演的出版商。

not in

错误的输出。不应包括WingNut,因为它既没有Ben Affleck也没有Peter Peter Jackson担任董事。

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

这是正确的查询,每个查询都使用| publisher | | ----------- | | WingNut | | Century Fox | | DC |

模拟
min(when true then 1 else 0) = 1

正确的输出,应仅显示DC和Century Fox。因为他们是唯一同时雇用本·阿弗莱克和彼得·杰克逊的出版商。

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
;

实时测试:https://www.db-fiddle.com/f/aDDw4Pd1DJzs6J5HgbKbdh/4

答案 4 :(得分:0)

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;