昨天,在调试其他开发人员代码时,我遇到了以下查询(将此查询称为Q1)
select b.id from b left join a on b.a_id = a.id where a.id is null
表a有一个字段:
id primary key
表b有两个字段:
id primary key
a_id foreign key (reference a.id)
当我跑到查询(Q1)之上时,它给了我结果:所有b.id,其中b.a_id在a.id中不可用
但我不明白上面的查询(Q1),特别是“where”子句。如果我们检查a.id是否为空,那么它如何匹配以找到b表中不可用的那些。
此外,Q1和Q2(查询下方)之间有什么区别:
select b.id from b where not exists (select * from a where a.id = b.a_id)
我甚至不理解Q2。
答案 0 :(得分:1)
Q1中的关键点是left join
。你可能知道 LEFT JOIN关键字返回左表(Customers)中的所有行,即使右表(Orders)中没有匹配项也是如此。
旁注:正如您所提到的,id是表a的主键,所以它不是null,而where子句永远不会是真的!
在Q2中,您只选择那些在表a中具有相应值的记录。
您可以找到更多信息here
答案 1 :(得分:1)
第一个查询,Q1:
我相信你会再次看到这一点,这并不罕见。 left join
将获取左表中的所有记录,以及右侧与参考相匹配的记录。但是where
子句正在做的是将其反转,以便它只选择否匹配的位置,即a.id is null
。
您可以将其放在更具描述性的文字中:
第二个查询Q2 会给你相同的结果,但这是另一种写作方式。基本上是这样的:
关于两个查询的效果,您可以read more here。它基本上归结为“它取决于”。通常情况下,exists
会有非常好的表现,但由于我们在前面添加not
,因此仍然需要检查每条记录。
编辑:
关于这些查询有一个很好的写作,还有一些其他的编写方法,在这里:What's the difference between NOT EXISTS vs. NOT IN vs. LEFT JOIN WHERE IS NULL?
答案 2 :(得分:1)
select b.id from b
这很简单,结果就是表b中的所有行。
select b.id from b left join a on b.a_id = a.id
请注意,由于left join
,此结果中的记录数与第一步相同。要了解left join
,您可以尝试inner join
:
select b.id from b inner join a on b.a_id = a.id
行数应该更少,因为inner join
只返回来自表b并加入a的行。因此a_id不在表a中的记录将不会显示。 但是离开加入不要这样做!左连接将显示表b中的所有记录,即使它不存在于表a中的记录,并且无法与表a连接的记录将保留为空。
这就是为什么会出现第三步的原因
添加空检查条件
select b.id from b left join a on b.a_id = a.id where a.id is null