我有两个包含相同密钥p_id的表:
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
(p_id
中未找到test2
,因此b.p_id
为{ {1}} - null
不在subselect中 - 因此结果仍为1行。
有人可以解释一下这种行为吗?
答案 0 :(得分:2)
它与比较中如何处理NULL有关。
要测试/查看,您可以运行简单的查询,例如:
SELECT 1 来自双重 WHERE NULL = NULL;
SELECT 1 来自双重 WHERE NULL NOT IN(1,2,3);
既不返回一行,因为两个条件都返回NULL,这是"不是真的"。
答案 1 :(得分: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;
它更干净,效率更高。
答案 2 :(得分: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
< / p>
执行此操作的正确方法是使用NOT EXISTS
。同时处理NULL
个值
select *
from test1 a
Where Not Exists (select 1 from test2 b Where a.p_id = b.p_id)
答案 3 :(得分:0)
通常,首先执行WHERE,然后执行JOIN。此外,当你使用LEFT JOIN时,LEFT表包含所有内容,所以你不应该期望在JOIN之后有一行p_id = 2,正如你所说的那样。