IN&EXISTS与NOT IN&NOT EXISTS 的优化原则小结

1. exists的执行流程

select * from t1 where exists ( select null from t2 where y = x )

可以理解为:


复制代码 代码如下:

for x in ( select * from t1 )

loop

if ( exists ( select null from t2 where y = x.x )

then

output the record

end if

end loop

对于in 和 exists的性能区别:

如果子查询得出的结果集记录较少,主查询中的表较大且又有索引时应该用in,反之如果外层的主查询记录较少,子查询中的表大,又有索引时使用exists。

其实我们区分in和exists主要是造成了驱动顺序的改变(这是性能变化的关键),如果是exists,那么以外层表为驱动表,先被访问,如果是in,那么先执行子查询,所以我们会以驱动表的快速返回为目标,那么就会考虑到索引及结果集的关系了

另外in时不对null进行处理,如:

select 1 from dual where null in (0,1,2,null)

结果为空。

2. not in 与not exists:
not exists的执行流程


复制代码 代码如下:

select …..

from rollup r

where not exists ( select ‘found’ from title t

where r.source_id = t.title_id);

可以理解为:


复制代码 代码如下:

for x in ( select * from rollup )

loop

if ( not exists ( that query ) ) then

output

end if;

end;

注意:not exists 与 not in 不能完全互相替换,看具体的需求。如果选择的列可以为空,则不能被替换。

例如下面语句,看他们的区别:

select x,y from t;

x y

—— ——

1 3

3 1

1 2

1 1

3 1

5

select * from t where x not in (select y from t t2 )

no rows

select * from t where not exists (select null from t t2

where t2.y=t.x )

x y

—— ——

5 null

所以要具体需求来决定

对于not in 和 not exists的性能区别:

not in 只有当子查询中,select 关键字后的字段有not null约束或者有这种暗示时用not in,另外如果主查询中表大,子查询中的表小但是记录多,则应当使用not in,并使用anti hash join.

如果主查询表中记录少,子查询表中记录多,并有索引,可以使用not exists,另外not in最好也可以用/*+ hash_aj */或者外连接+is null

not in 在基于成本的应用中较好

比如:


复制代码 代码如下:

select …..

from rollup r

where not exists ( select ‘found’ from title t

where r.source_id = t.title_id);

改成(佳)

select ……

from title t, rollup r

where r.source_id = t.title_id(+)

and t.title_id is null;

或者(佳)

sql> select /*+ hash_aj */ …

from rollup r

where ource_id not in ( select ource_id

from title t

where ource_id is not null )

注意:上面只是从理论上提出了一些建议,最好的原则是大家在上面的基础上,能够使用执行计划来分析,得出最佳的语句的写法。

‘//=============================


exists,not exists总结

1 exists

select * from anken_m where exists(

select my_list_temp_m.sales_code

from my_list_temp_m

where my_list_temp_m.sales_code=anken_m.sales_code)

说明:

1) 查询在anken_m表和my_list_temp_m表中都存在的sales_code。

2) sales_code是anken_m的主键,my_list_temp_m的外键。

注意:

1) 外层查询表anken_m是查询的对象。

2) 内层查询表my_list_temp_m是条件对象。

3) 内外层的查询表不能相同。

4) 作为关联条件的anken_m表不需要在内层查询from后添加。

5) my_list_temp_m.sales_code=anken_m.sales_code条件的左右顺序不影响查询结果。

2 not exists
select * from anken_m where not exists(

select my_list_temp_m.sales_code

from my_list_temp_m

where my_list_temp_m.sales_code=anken_m.sales_code)

说明:

1) 查询在anken_m表中存在,但是在my_list_temp_m表中不存在的sales_code。

2) sales_code是anken_m的主键,my_list_temp_m的外键。

注意:

1) 外层查询表anken_m是查询的对象。

2) 内层查询表my_list_temp_m是条件对象。

3) 内外层的查询表不能相同。

4) 作为关联条件的anken_m表不需要在内层查询from后添加。

5) my_list_temp_m.sales_code=anken_m.sales_code条件的左右顺序不影响查询结果。

3 综合运用
update anken_m

set(plan_type_code, branch_name, business_type_code)

=(select anken.plan_type_code,anken.branch_name,anken.business_type_code

from anken

where anken.sales_code=anken_m.sales_code)

where exists (

select anken.sales_code

from anken,my_list_temp_m

where my_list_temp_m.sales_code=anken.sales_code

and anken.sales_code=anken_m.sales_code

)

说明:

1) 用一个表的记录数据更新另一个表的记录数据。

2) 用一个sql语句进行批量更新。

2) sales_code是anken,anken_m的主键,my_list_temp_m的外键。

注意:

1) set 语句中的要被更新字段必须跟数据源字段一一对应,另外数据源查询中的条件必须限定一条记录。也就是根据sales_code可以唯一确定anken的一条记录,和anken_m的一条记录,这样才能保证要被更新的记录和数据源记录的主键是相同的。

2) 根据where exists语句可以确定数据源记录的范围,也就是可以用anken表中哪些记录更新anken_m表。所以anken_m不需要在where exists语句中的from后添加。

(0)
上一篇 2022年3月21日
下一篇 2022年3月21日

相关推荐