我一直在撕扯我的头发:
-- misses nulls
SELECT *
FROM BASE_TABLE TAB1
FULL JOIN BASE_TABLE TAB2
USING (ANOTHER_ID)
WHERE (TAB1.ID = 6 OR TAB1.ID IS NULL)
AND (TAB2.ID = 8 OR TAB2.ID IS NULL);
-- catches nulls
SELECT *
FROM (SELECT * FROM BASE_TABLE WHERE ID = 6) TAB1
FULL JOIN (SELECT * FROM BASE_TABLE WHERE ID = 8) TAB2
USING (ANOTHER_ID);
第一个查询丢失了一个或另一个表中的行不存在的行。为什么第一个查询无法进行外连接?
我一直以为我已经得到它了 - 首先评估WHERE子句,因此以后不会应用'OR IS NULL' - 但这对我来说没有意义,因为我已成功应用'IS NULL'过去的谓词在连接后选择行。
我想让第一个查询工作,出于性能原因 - 有谁知道问题是什么?
答案 0 :(得分:3)
第一个查询执行连接,然后执行过滤,第二个查询执行过滤,然后执行连接。 使用外连接时,区别很重要。
您可以通过一些示例数据了解它。
create table tab1 (id number, another_id number);
create table tab2 (id number, another_id number);
insert into tab1 values (6,5);
insert into tab2 values (8,5);
insert into tab1 values (1,6);
insert into tab2 values (2,6);
SELECT *
FROM TAB1
FULL JOIN TAB2 USING (ANOTHER_ID);
ANOTHER_ID ID ID
--------------- --------------- ---------------
5.00 6.00 8.00
6.00 1.00 2.00
结果集(没有WHERE)显示another_id 6的连接条件已成功。不需要外连接。
当您添加WHERE过滤器时,它会过滤掉6的匹配,因为ID既不是6,8也不是null。也就是说,您已将其用作过滤谓词而非连接谓词。
我相信你的意图是TAB1加入TAB2时应该在ANOTHER_ID上匹配加上TAB1的ID应该是6,TAB2的ID应该是8。这就是第二个SQL中的内容。
连接谓词也可以表示为
SELECT *
FROM TAB1
FULL JOIN TAB2 ON
(TAB1.ANOTHER_ID = TAB2.ANOTHER_ID AND TAB1.ID=6 AND TAB2.ID=8)
答案 1 :(得分:0)
嗯。这是一个很好的脑力激荡,但我想我可能会有它。
在第一个查询中,您将表格与ANOTHER_ID键完全结合在一起。
当两个连接的表相同时,无论是执行完全连接,内部连接,左连接还是右连接都无关紧要:结果是相同的。因为您的密钥ANOTHER_ID在两个表中始终存在或不存在。在任何情况下,没有一个表具有在另一个表中找不到的ANOTHER_ID值,因此不存在TAB1.ANOTHER_ID或TAB2.ANOTHER_ID最终为NULL的情况。所以你实际上只是在ANOTHER_ID上进行自我内部联接。
现在我不知道你的ID列的内容,但我想它总是充满了一些价值。因此,在自我内部联接之后,生成的ID列将始终填充某些内容。也许不是6或8,但也不是NULL。如果没有NULL值,您的WHERE查询将转换为WHERE TAB1.ID = 6 AND TAB2.ID = 8
,只会留下正确的组合,而不会留下任何其他组合。
相反,在第二个查询中,您要定义ID = 6和ID = 8的子集,并将这些子集完全加入到另一个子集中。子集1包含子集2中不存在的某些ANOTHER_ID,反之亦然。所以现在有一个FULL JOIN的基础,因为某些行不会连接到其他行,在TAB1.ID或TAB2.ID中留下可以检测到的NULL值。
我认为可以通过将WHERE子句更改为:WHERE TAB1.ID IN (6,8) AND TAB2.ID IN (6,8)
来调整您的第一个查询。然而,这将作为您的第二个查询给出不同的结果,并且我认为还有一定数量的重复行。而且我也认为它不会更快。