2017-06-14 90 views
1

我有一个同时包含相同的密钥的p_id 2个表:SQL加入()不工作

test1      test2 
+-------------+    +----------------------+ 
| p_id | name |    | o_id | name | p_id | 
+-------------+    +----------------------+ 
| 1 | Paul |    | 1 | London | 1 | 
| 2 | Marc |    | 2 | Paris | 1 |   
+-------------+    +----------------------+ 

现在我想从TEST1有没有关系,Test2的所有条目。 在上面的例子中,我抽象了我的表,所以RIGHT JOIN是不可能的(实际上我必须加入4个表格)。

SELECT a.*,b.* 
FROM test1 a 
LEFT JOIN test2 b 
ON a.p_id=b.p_id 
WHERE b.p_id NOT IN(SELECT DISTINCT p_id FROM test2); 

我预计有一行p_id=2。但是我得到一个空的结果。 当我将我的代码更改为:

SELECT a.*,b.* 
FROM test1 a 
LEFT JOIN test2 b 
ON a.p_id=b.p_id 
WHERE a.p_id NOT IN(SELECT DISTINCT p_id FROM test2); 

然后它工作正常。但为什么?我想LEFT JOIN首先处理(1行作为结果)和WHERE被处理后(JOIN一直没有找到p_idtest2所以b.p_idnull - null是不是在子查询 - 所以还是1行作为结果)。

有人可以解释这种行为吗?

+0

我没有看到任何问题。您的LEFT JOIN在第二个表格中获得p_id = 1的2行但是,您将子查询中的那些排除 – Mihai

+0

@RaymondNijland:对不起,但这是不正确的。我检查了你的sugesstion,现在我得到了2行,其中p_id和test1.name被设置,其他列为空。 – zuluk

+0

@Mihai:这是不正确的。左连接的结果是3行(2个p_id = 1,1个p_id = 2),然后我用p_id = 1排除两者。 – zuluk

回答

2

它与如何在比较中处理NULL有关。

要测试/看,你可以运行简单的查询,如:

选择1 FROM DUAL WHERE NULL = NULL;

SELECT 1 FROM DUAL WHERE NULL NOT IN(1,2,3);

既不返回一行,因为两个条件都返回“不正确”的NULL。

+0

你知道这是否特定于MySql?或者这也适用于例如甲骨文? – zuluk

+0

我有一段时间没有和甲骨文玩过,然后只有一门大学课程。我相信MySQL中可能会有一些设置来改变这种行为,所以即使对于MySQL也可能并非总是如此。与这些类似的查询应该可以有效地找出几乎所有的SQL RDBMS。 – Uueerdo

2

正如Uueerdo所说,这是一个NULL比较问题。但是,除此之外,你应该真的使用反连接:

SELECT a.*,b.* 
FROM test1 a 
LEFT JOIN test2 b 
ON a.p_id=b.p_id 
WHERE b.p_id IS NULL; 

它更干净,通常更有效。

+0

虽然不存在可能会更快。 – xQbert

+0

@xQbert相当古老但有趣的阅读在这里:https://explainextended.com/2009/09/18/not-in-vs-not-exists-vs-left-join-is-null-mysql/ –

1

NOT IN没有错,因为您正在筛选Where子句中的右表,因此它被隐式转换为INNER JOIN

没有Where子句结果将是这样

+------+------+--------+--------+--------+ 
| p_id | name | o_id | name | p_id | 
+------+------+--------+--------+--------+ 
| 1 | Paul | 1  | London | 1  | 
| 1 | Paul | 2  | Paris | 1  | 
| 2 | Marc | (null) | (null) | (null) | 
+------+------+--------+--------+--------+ 

在此,如果要应用的滤波器

WHERE b.p_id NOT IN(SELECT DISTINCT p_id FROM test2); 

子查询返回1,其存在于上述最后一列结果。所以你没有得到任何结果。

如果您想知道为什么NULL最后一条记录不是因为它不是1而没有返回。这是因为NULL不能比较使用=,IN,NOT IN等。我们需要使用IS运营商进行检查NULL

正确的做法是使用NOT EXISTS。处理NULL的值也是

select * 
from test1 a 
Where Not Exists (select 1 from test2 b Where a.p_id = b.p_id) 
+0

不在doens处理好空洞。另外,在左连接右侧的表上使用where子句否定了左连接,使其表现得像内部连接。你最好使用反连接或不存在。 – xQbert

+0

@xQbert - 没有我提到它作为答案。刚才解释了为什么它没有返回结果。 –

+0

@xQbert - 更新了我的答案 –

0

通常,首先执行WHERE,然后执行JOIN。另外,当你使用LEFT JOIN时,它是LEFT表中包含的所有内容,所以在JOIN之后,你不应该期望p_id = 2的一行,正如你所说的那样。

+0

这似乎是不正确的:https://dba.stackexchange.com/questions/5038/sql-server-join-where-processing-order该链接是关于MSDN,但我认为它在MySQL中是一样的。 – zuluk