哪一个更好:加入两个表时过滤连接子句或过滤子句子句?

时间:2015-01-09 02:44:29

标签: sql-server database

我正在尝试在MS SQL Server上加入2个或更多表。所有表都有IsActive字段,用于确定活动记录。 (IsActive = 1表示活动记录,IsActive = 0表示已从系统中删除非活动记录或记录)

所以我有两个加入两个或更多表的条件。

在第一个查询中,我过滤了连接子句

上的IsActive
select * from table_A a
inner join table_B b
on a.ID = b.ID and b.IsActive = 1
inner join table_C c
on b.ID = c.ID and c.IsActive = 1
where a.IsActive = 1

On第二个查询,我也可以在where子句

上过滤IsActive
select * from table_A a
inner join table_B b
on a.ID = b.ID 
inner join table_C c
on b.ID = c.ID 
where a.IsActive = 1 and b.IsActive = 1
and c.IsActive = 1

注意:表A到B的关系是一对一的,但是从表A到C是一对多,并且所有表都在主键ID上有聚簇索引,ID是自动增量。

那么你认为哪一个更好? (假设每个表有大约100.000条记录(80%活动记录和20%非活动记录))

由于

1 个答案:

答案 0 :(得分:1)

区别很简单但需要仔细观察。

考虑以下示例:

create table tbl_client as
    select 1 as client_id, 'aaa' as client_name, 'Y' is_active from dual
    union all
    select 2, 'bbbbb', 'N' from dual
    union all
    select 3, 'cc', 'Y' from dual;

create table tbl_transaction as
    select 1 transaction_id, 1 client_id, 123.34 amount from dual
    union all
    select 2, 1, 4353.45 from dual
    union all
    select 3, 2, 251.48 from dual;

现在,在这些表上运行后续查询:

内部加入

在内部联接中,以下两个查询的结果没有区别:

select c.client_name, t.amount, t.is_paid
  from tbl_client c
       inner join tbl_transaction t
           on     c.client_id = t.client_id
              and t.is_paid = 'Y'; -- filter on join

select c.client_name, t.amount, t.is_paid
  from tbl_client c
       inner join tbl_transaction t
           on     c.client_id = t.client_id
 where t.is_paid = 'Y'; -- filter in where

他们的结果都与:

相同
CLIENT_NAME     AMOUNT IS_PAID
----------- ---------- -------
aaa             123.34 Y      
aaa            4353.45 Y

左外连接

这就是差异所在。

考虑以下问题:

select c.client_name, t.amount, t.is_paid
  from tbl_client c
       left outer join tbl_transaction t
           on     c.client_id = t.client_id
              and t.is_paid = 'Y'; -- << filter in join

结果:

CLIENT_NAME     AMOUNT IS_PAID
----------- ---------- -------
aaa             123.34 Y      
aaa            4353.45 Y      
cc                             -- << Note that client cc's transaction record is not there
bbbbb                          -- << and this client also shows up

当您在左外连接中的where上应用过滤器时:

select c.client_name, t.amount, t.is_paid
  from tbl_client c
       left outer join tbl_transaction t
           on     c.client_id = t.client_id
 where t.is_paid = 'Y';       -- << filter in where

结果:

CLIENT_NAME     AMOUNT IS_PAID
----------- ---------- -------
aaa             123.34 Y      
aaa            4353.45 Y      -- No row for bbbbb or cc clients, just like the inner join

<强>摘要

简而言之,当您在连接条件上放置过滤器时,过滤器将应用于要连接的表。例如,在左外连接部分的第一种情况下,tbl_transaction的行没有显示给客户bbbbb

但是当您在where子句中放置过滤器时,它会过滤加入所有表后检索的整个数据集(逻辑上。内部技术操作因RDBMS而异)。这就是bbbbbcc的行未显示在上一个查询中的原因。

<强> Fiddle

修改

正如@DanGuzmanSqlServerMvp在他的评论中提到的,对于您在问题中发布的示例,SQL Server查询优化器应该执行相同的计划。但是,如果查询中存在外部联接,则计划会有所不同。