笛卡尔积(Cartesian join)
当一个SQL中两个表无关联条件,或者关联条件可以被忽略的时候就会发生笛卡尔积。遇到这种情况要仔细分析SQL是否符合需求,是不是SQL写错了。在有些情况下,如果 CBO预估两个表返回的行数很少(通常小于10),那么也可能发生笛尔积。遇到这样的情况。
1.如果SQL跑得很快,并且两个表返回的真实行数确实很少,那么走笛卡尔积可能是最优化的,这个时候我们不用管。只要是SQL跑得快我们都不用管
2.统计信息不准确导致CBO错误估算,从而引发笛卡尔积连接。这时要更新统计信息。
3.CBO计算返回行数错误,导致笛卡尔积。CBO计算返回行数错误经常发生,因为CBO是基于一些数学公式的,出错是很正常。这个时候我们可以用HINT更正执行计划。
在做SQL优化的时候,要注意MERGE JOIN CARTESIAN关键字,一旦发现有笛卡尔积仔细分析到底是哪些原因引发
执行计划中有MERGE JOIN CARTESIAN看关键下面的返回记录数(看表真实有多少行select count(*)一把)
外连接(outer join)
外连接有三种,left outer join,right outer join,full outer join.
左外连接,左表就做驱动表,右外连接,右表就做驱动表
如果SQL是用ORACLE:加号的另一边的表做驱动表
左外连接,左表就做驱动表,右外连接,右表就做驱动表 ---在NL的时候
如果走HASH JOIN,没有这种限制
select ename,sal,job,dname,dept.deptno
from emp right outer join dept on emp.deptno=dept.deptno in (10,20,40);
让它走NL 驱动表为dept
让它走Hash join 并且让emp为驱动表 *+swap_join_inputs(emp) use_hash(emp,dept)*/用此hint
这个hint还有一个作用
就是多个表join的时候 能指定哪个表作为驱动表
hash join驱动表是放在PGA中 PGA中一个进程最多2G ,被驱动表不会进PGA 只有驱动表进去
SORT MERGE JOIN也要分的:先排序然后+NL
总结:
如果 SQL当中有外连接 + 如何优化:首先看连接方式,然后看驱动表(小表作为驱动表)
半连接(semi-join)
半连接是指两个表/结果集做join,但是只返回某一个表/结果集中的数据。
执行计划中,看到有NESTED LOOPS SEMI/HASH JOIN SEMI 就表示有半连接 in exists都是半连接
比如下面的SQL(基于HR oracle11gR2)
select department_name from hr.departments dept
where department_id in(select department_id from hr.employees emp);
该SQL是departments 表和employee表进行JOIN,但是只返回departments表中的数据。你们肯定在网上看到过相关的理论,in可以被exists代替,现在用exists改写
select department_name from hr.deparments dept where exists (select null from hr.employees emp
where emp.department_id =dept.department_id);
还可以用join改写这个SQL
select distinct department_name from hr.departments dept,hr.employees emp
where dept.department_id=emp.department_id;
semi join SQL如果改写为inner join,一定要记得去重,这里也给了我们一个思路,当你看到SQL是inner join,并且用了distinct,并且只从一个表中取数据,可以将这类SQL改写为半连接,避免distinct排序,提升性能。
in和exists一般情况下,执行计划是一样的,当SQL很复杂,用in 或者是exists不同的写法对性能就会产生巨大影响反连接(anti-join)
反连接其实是特殊的半连接。只是把in/exists换成了not in/not exists,执行计划中,看到有NETED ANTI/HASH JOIN ANTI就表有反连接
例子:
select department_name from hr.deparments dept where department_id not in (select department_id from hr.employees emp);
select department_name from hr.departments dept where not exists (select null from hr.employees emp where emp.deparment_id=dept.department_id);
以上两个不等价:
not in 不返回结果,not exists返回16行。这里有说明not in 和not exists 不能像 in和exists那样随意改写SQL为什么用not in 不返回结果呢?因为子查询 select department_id from hr.employees emp会返回Null值。oracle有个缺陷:in里面有Null值会返回结果,比如下面SQL
SQL>select department_name from hr.departments dept where department_id in (10,50,null);
但是Not in 里面有Null就不会返回结果,直接返回Null
SQL>select department_name from hr.departments dept where department_id not in(10,50,null);
not in 里面有Null值
我们在做SQL优化的时候,一定要注意Not in和not exists是不能等价转换。
当然,一般情况下,Not in子查询里面都会排除有Null的情况,不然查询结果没意义。现在来过滤掉Null值
SQL>select department_name from hr.department dept where department_id not in (select department_id from hr.employees emp where department_id is not null);
A LEFT JOIN B 是不是说 A有,并且B没有 ,B用Null代替 那我加个where 条件 is null,B.XXX is null 是不是说明 A与B没关联上是不是等效于Not in,not exists的效果
改写:select department_name from hr.departments dept,hr.employees emp where dept.department_id=emp.department_id(+) and emp.department_id is null;




