【问题标题】:Oracle full outer join with filter (ANSI) not working as expected带有过滤器 (ANSI) 的 Oracle 完全外连接未按预期工作
【发布时间】:2013-06-22 10:53:52
【问题描述】:

我遇到了一个问题,即带有过滤器的 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

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

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 优化器或驱动程序的问题??

【问题讨论】:

  • 当您使用完全外连接时,您要求数据库连接所有记录并保留所有不满足连接条件的记录。在您的示例中,valid='Y' 是条件的一部分,因此其他两条记录保持未连接状态。这是预期的结果。
  • 没错,valid = 'Y' 条件应该在where 子句中指定,而不是on 子句中。
  • GriffeyDog - valid = 'Y' 的 where 子句不会返回 Mrs. Piggy 的最后一条记录,所以这也不起作用
  • @haki - 我不认为我跟随。如果我在左连接中提供valid = 'Y',它只会返回带有“Y”的有效记录。在这个外部连接中,它也返回不遵循我在 ON 子句中放入的“N”条记录

标签: oracle filter oracle11g ansi outer-join


【解决方案1】:

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

在完全外连接中,您将根据 m.id 和 p.valid 从左到右连接,但您也将返回右侧所有不连接任何内容的行以及左侧所有不连接的行'不要加入任何东西。

【讨论】:

  • 我想要左右两边。我可以联合一个左连接查询和一个右连接查询,但期望一个完整的外部连接能够处理过滤
  • 我稍微误读了您的问题,但 ON 子句不是过滤器 - 它是您将左右表粘合在一起的方式。您选择的连接类型(左/右/全)是(排序)过滤器
  • 确定 ON 子句是您将它们连接在一起的方式,但您可以在 ON 子句中添加过滤值以限制内部/左/右连接中返回/连接的内容。我只是没有看到这在外部连接中起作用
【解决方案2】:

正如其他人在 cmets 中解释的那样,这是因为您将过滤器逻辑放置在哪里。因此,您的原始查询将返回所有布偶和所有电话号码,并在 ID 匹配且有效 = 'Y' 时将它们显示为已加入。所以这就是为什么您会看到所有电话号码,但只匹配有效号码。

您可以采用您已经想出的方法,也可以将“有效”逻辑移到 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 'Y' 或电话表不匹配的行。如果您只将 where 子句设为“P.VALID = 'Y'”,那么您将有效地将其转换为右外连接。

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

【讨论】:

  • 啊!我认为现在一切都说得通了。谢谢
猜你喜欢
  • 2013-08-06
  • 2011-05-22
  • 1970-01-01
  • 2014-05-15
  • 1970-01-01
  • 1970-01-01
  • 2015-06-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多