Sql 在左连接中放置条件的背后是什么

Sql 在左连接中放置条件的背后是什么,sql,oracle,Sql,Oracle,我有两个类似的select语句,它们返回不同数量的行 选择1: select count(*) from table_1 a left join table_2 b on a.column_1 = b.column_1 and b.column_2 = 1 where b.column_1 is null and a.column_2 = 2; 76782 rows returned 选择2: select count(*) from tab

我有两个类似的select语句,它们返回不同数量的行

选择1:

select count(*)
from table_1 a
    left join table_2 b
        on a.column_1 = b.column_1 
        and b.column_2 = 1
where b.column_1 is null
    and a.column_2 = 2;

76782 rows returned
选择2:

select count(*)
from table_1 a
    left join table_2 b
        on a.column_1 = b.column_1
        and a.column_2 = 2
        and b.column_2 = 1
where b.column_1 is null;

307778 rows returned
DB版本是Oracle 12.2.0.1.0

有人能解释一下返回不同行数的两个select语句的背后是什么吗?

select 1:

select count(*)
from table_1 a
where not exists (select 1 
                  from table_2 b
                  where a.column_1 = b.column_1 
                    and b.column_2 = 1)
  and a.column_2 = 2;
条件b.列_2=1在左连接之前应用于表_1, 在应用所有外部联接后,将条件a.column_2=2和b.column_1为null应用于数据集。 即,查询执行以下操作:

循环遍历满足条件table_1.column_2=2的所有table_1行,然后 尝试查找满足条件table_2.column_2=1的table_2行,并将它们与列_1上的table_1左外连接,然后 筛选结果行,以便仅保留满足外部联接后条件表_2的行。列_1为空。 选择2:

select count(*)
from table_1 a
where not exists (select 1 
                  from table_2 b
                  where a.column_1 = b.column_1 
                    and b.column_2 = 1
                    and a.column_2 = 2);
条件b.列_2=1在左连接之前应用于表_1, 条件a.column_1=2充当连接谓词,即它表示仅当a.column_1=2时才查找匹配行, 条件b.column_1为null将在应用所有外部联接后应用于数据集。 即,查询执行以下操作:

循环遍历满足条件table_1.column_2=2的所有table_1行,然后 尝试查找满足条件table_2.column_2=1的table_2行,并将它们与列_1上的table_1进行左外连接,但仅限于满足条件table_1.column_1=2的table_1行, 筛选结果行,以便仅保留满足外部联接后条件表_2的行。列_1为空 注:

事实上,在查看查询的执行计划时,可以最好地检查幕后发生的事情,将注意力更多地集中在查询的访问/筛选谓词部分。实际上,这是一个很好的一般性建议:当您不了解所看到的查询的某些内容时,请查看其执行计划,这是一种更具命令性的语言,如。

第一个过滤器使用a.column_2=2,因为它位于where子句中

第二个在a上没有过滤器。为什么?因为a.column_2=2。即使这不是真的,也会返回一行,因为这是以a作为第一个表的左联接

但是,应该发生的是,对于非2行,a中的任何列都为NULL。

两个查询都在执行反联接,其中b.column_1为NULL将排除表_1中与联接条件匹配的所有行。将条件移动到联接中会使联接更具限制性,因此反联接排除的行更少

有时,我发现将反连接重新编写为不存在更直观:

选择1:

select count(*)
from table_1 a
where not exists (select 1 
                  from table_2 b
                  where a.column_1 = b.column_1 
                    and b.column_2 = 1)
  and a.column_2 = 2;
选择2:

select count(*)
from table_1 a
where not exists (select 1 
                  from table_2 b
                  where a.column_1 = b.column_1 
                    and b.column_2 = 1
                    and a.column_2 = 2);
您可以将其改写为:

select count(*)
from table_1 a
where (a.column_2 = 2 
       and not exists (select 1 
                  from table_2 b
                  where a.column_1 = b.column_1 
                    and b.column_2 = 1)
      )
   OR a.column_2 <> 2;

这显然比Select 1限制性小得多。

如果在where子句中放置左连接表的非空条件,则它将变成一个内部连接