相关子查询可以跟随null检查是否可以用连接替换?

时间:2017-08-20 15:54:57

标签: sql sql-server tsql

这个相关的子查询,子查询后面的子查询是不是对外键的空检查,是否用连接替换?例如:

select * from TableABC as t
where 
(t.label_id is null or t.label_id in ( select t1.id from Labels as t1 where t1.type = '123'))
and
(t.tag_id is null or t.tag_id in ( select t2.id from Tags as t2 where t2.type = '123'))

用文字描述:让我们说,我正在查找所有记录,如果他们已经定义了标签参考,那么标签必须是某种类型;同样适用于标签

或者可以通过其他方式改进此查询?

适用于TSQL(MS SQL)。

更新:
我已经添加了HABO提示的表别名。希望它会提高可读性。

3 个答案:

答案 0 :(得分:2)

我倾向于把它写成:

select t.*
from TableABC abc
where (abc.label_id is null or
       exists (select 1 from labels where l.id = abc.label_id and l.type = 123)
      ) and
      (abc.tag_id is null or
       exists (select 1 from tags t where t.id = abc.tag_id and t.type = 123)
      );

然后我确定我在labels(id, type)tags(id, type)的索引中(如果id不是主键)。

但是,您的版本可能还有一个合理的执行计划和正确的索引。

答案 1 :(得分:1)

我不确定这是一项改进,但确实使用了left outer join而不是相关的子查询。

-- Sample data.
declare @TableABC as Table( ABCId Int Identity, LabelId Int, TagId Int );
declare @Labels as Table( LabelId Int Identity, Label VarChar(3) );
declare @Tags as Table( TagId Int Identity, Tag VarChar(3) );

insert into @Labels ( Label ) values ( '123' ), ( '12' ), ( '123' );
insert into @Tags ( Tag ) values ( '123' ), ( '213' ), ( '123' ), ( '312' );
insert into @TableABC ( LabelId, TagId ) values
  ( 1, 1 ), ( 1, 2 ), ( 1, 3 ), ( 1, 4 ),
  ( 2, 1 ), ( 2, 2 ), ( 2, 3 ), ( 2, 4 ),
  ( 3, 1 ), ( 3, 2 ), ( 3, 3 ), ( 3, 4 ),
  ( NULL, 1 ), ( NULL, 3 ), ( 1, NULL ), ( 3, NULL ), ( NULL, NULL );

select ABC.ABCId, ABC.LabelId, ABC.TagId,
  L.LabelId as L_LabelId, L.Label as L_Label,
  case when ABC.LabelId is NULL or L.Label = '123' then '<<<' else '' end as 'L_Match',
  T.TagId as T_TagId, T.Tag as T_Tag,
  case when ABC.TagId is NULL or T.Tag = '123' then '<<<' else '' end as 'T_Match'
  from @TableABC as ABC left outer join
    @Labels as L on L.LabelId = ABC.LabelId left outer join
    @Tags as T on T.TagId = ABC.TagId;

-- "Original" query:
select *
  from @TableABC
  where ( LabelId is null or LabelId in ( select LabelId from @Labels where Label = '123' ) ) and
    ( TagId is null or TagId in ( select TagId from @Tags where Tag = '123' ) );

-- Left outer joins:
select ABC.*
  from @TableABC as ABC left outer join
    @Labels as L on L.LabelId = ABC.LabelId and L.Label = '123' left outer join
    @Tags as T on T.TagId = ABC.TagId and T.Tag = '123'
  where ( ABC.LabelId is NULL or L.LabelId is not NULL ) and ( ABC.TagId is NULL or T.TagId is not NULL );

提示:始终将有用的表别名与连接一起使用,并将它们应用于所有列。

答案 2 :(得分:0)

使用联盟

on TableABC.label_id = Lables.id or TableABC.label_id is null

ec2-metadata会返回太多行