我遇到了一个问题,即带有过滤器的 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 优化器或驱动程序的问题?
最佳答案
正如其他人在评论中所解释的那样,这是因为您放置过滤器逻辑的位置。因此,您的原始查询将返回所有布偶和所有电话号码,并在 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 子句中。关于Oracle 带过滤器的全外连接 (ANSI) 未按预期工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17300789/