带有过滤器(ANSI)的Oracle全外连接无法按预期工作

时间:2013-06-25 14:58:45

标签: oracle filter oracle11g ansi outer-join

我遇到了一个问题,其中带有过滤器的Oracle ANSI全外连接没有按照我期望的那样返回结果。我已经创建了一个快速示例来解释我正在做什么并看到......

Table 1 - MUPPET

ID  NAME    
1   Kermit the Frog    
2   Fozzie Bear    
3   Mrs. Piggy    
4   Beaker    
5   Animal    
6   Swedish Chef

Table 2 - PHONE

ID  MUPPET_ID      PHONE    VALID
1   1      1111111111      Y
2   1      2222222222      N
3   2      3333333333      Y
4   4      4444444444      Y
5   5      5555555555      Y
6   6      6666666666      Y
7   6      7777777777      N
8   8      8888888888      Y

从这些表中我想选择所有的muppets和所有有效的电话号码。我想要所有的布偶,无论他们是否有电话号码,我想选择所有有效的电话号码,无论他们是否与布偶相关联。这是我希望工作的查询...

SELECT   m.id muppet_id,
         m.name,
         p.id phone_id,
         p.phone,
         p.valid
  FROM      muppet m
         FULL OUTER JOIN
            phone p
         ON (M.ID = P.MUPPET_ID AND P.VALID = 'Y')

但是这里的结果包括无效手机,即使我在外联接过滤器中没有指定无效手机

MUPPET_ID   NAME    PHONE_ID    PHONE   VALID

1   Kermit the Frog    1    1111111111  Y
                       2    2222222222  N
2   Fozzie Bear        3    3333333333  Y
4   Beaker             4    4444444444  Y
5   Animal             5    5555555555  Y
6   Swedish Chef       6    6666666666  Y
                       7    7777777777  N
                       8    8888888888  Y
3   Mrs. Piggy          

我能够在完全外连接的左侧部分使用子选择最终得到我想要的结果

SELECT   m.id muppet_id,
         m.name,
         p.id phone_id,
         p.phone,
         p.valid
  FROM      muppet m
         FULL OUTER JOIN
            (SELECT   id,
                      phone,
                      valid,
                      muppet_id
               FROM   phone
              WHERE   valid = 'Y') p
         ON (M.ID = P.MUPPET_ID) 

......结果......

MUPPET_ID   NAME      PHONE_ID  PHONE   VALID

1   Kermit the Frog           1 1111111111  Y
2   Fozzie Bear               3 3333333333  Y
4   Beaker                    4 4444444444  Y
5   Animal                    5 5555555555  Y
6   Swedish Chef              6 6666666666  Y
                              8 8888888888  Y
3   Mrs. Piggy

但我不明白为什么我要以这种方式查询。有人可以帮我解释为什么我的初始外连接查询与过滤器不能按预期工作?

编辑:

更有趣。当我运行此查询时,我按预期获得了6条记录

select valid from (
SELECT   m.id muppet_id,
         m.name,
         p.id phone_id,
         p.phone,
         p.valid
  FROM      muppet m
         FULL OUTER JOIN
            phone p
         ON (M.ID = P.MUPPET_ID AND P.VALID = 'Y')
) where valid = 'Y'

但是当我运行这个时,我没有得到任何记录

select valid from (
SELECT   m.id muppet_id,
         m.name,
         p.id phone_id,
         p.phone,
         p.valid
  FROM      muppet m
         FULL OUTER JOIN
            phone p
         ON (M.ID = P.MUPPET_ID AND P.VALID = 'Y')
) where valid <> 'Y'

可能这是Oracle优化器或驱动程序的问题??

2 个答案:

答案 0 :(得分:2)

正如其他人在评论中所解释的那样,这是因为您要放置过滤器逻辑。因此,当ID与AND valid ='Y'匹配时,原始查询将返回所有muppets和所有电话号码并将其显示为已加入。所以这就是为什么你看到所有的电话号码,但只匹配有效的电话号码。

您可以执行您已经想到的方法,也可以将“有效”逻辑移到WHERE子句中:

SELECT   m.id muppet_id,
         m.name,
         p.id phone_id,
         p.phone,
         p.valid
FROM      muppet m
         FULL OUTER JOIN
            phone p
         ON (M.ID = P.MUPPET_ID)
WHERE
         P.VALID = 'Y' or P.MUPPET_ID is null;

现在where子句正在丢弃VALID&lt;&gt;的行'Y'或电话桌不匹配的地方。如果你只使用where子句“P.VALID ='Y'”,那么你将有效地将其转换为右外连接。

那就是说,我实际上会选择你列出的第二个版本,因为如果该索引存在,这将允许你在VALID ='Y'上使用索引,其中上述版本可能由于OR而不会使用索引在where子句中。

答案 1 :(得分:0)

您可能应该使用左外连接而不是完全外连接。

在完全外部联接中,您将根据m.id和p.valid从左到右连接,但是您也将从右侧返回所有未加入任何行的行以及左侧所有行加入任何事情。