接上一篇:ORACLE碰到OR子查询效率低,三大方法助力性能起飞_PART2
1.ANTI JOIN OR SUBQUERY改写
对于ANTI JOIN OR SUBQUERY主要有两种形式:
1)ANTJOIN SUBQ1 OR ANTIJOIN SUQ2:这种可以用union改写,如果子查询来源于同一个表,也可以用集合转换改成anti join( cond1 and cond2)
2)ANTIJOIN(cond1 or cond2):这种用集合转换改成antijoin(cond1) and antijoin(cond2),不能用union改写,要用intersect.
利用集合操作,将OR转为AND,则可以unnest subquery,主要用于anti join转换
对于anti join改写比semi join稍微复杂点特别是子查询里有or的,不能简单改成union all,subq1 or subq2的这种可以改成union all,anti join(cond1 or cond2)的不能
改成union all。
另外对于anti join的,使用LNNVL或等价改写方法,也不能subquery unnest(semi join可以)。
OR能改为AND则能查询转换:
利用集合转换:
NOT (A OR B) 改为NOT A AND NOT B ==主要是这种,这种是anti join子查询里面有OR条件
NOT A OR NOT B改为 NOT (A AND B) ==这种如果A,B是子查询,也不能subquery unnest,如果是子查询里的条件,则可以
SQL1:subq1 or subq2形式,含义是满足条件OR其中任何一个分支的则返回,因此改写就相当于
两个分支UNION
这里主要研究anti join.
-
select * from a
-
where not exists
-
(select 1 from b where a.object_name = b.object_name)
-
or
-
not exists
-
(select 1 from c where a.object_id = c.object_id)
-
;
1037 rows selected.
非常慢,都是FILTER,可以对FILTER子节点建索引优化,这里可以对b.object_name和c.object_id
建立索引优化
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 2849596723
-
-
---------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
---------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 77378 | 7405K| 12M (1)| 41:20:01 |
-
|* 1 | FILTER | | | | | |
-
| 2 | TABLE ACCESS FULL| A | 77380 | 7405K| 307 (1)| 00:00:04 |
-
|* 3 | TABLE ACCESS FULL| B | 1 | 25 | 166 (1)| 00:00:02 |
-
|* 4 | TABLE ACCESS FULL| B | 1 | 5 | 306 (1)| 00:00:04 |
-
---------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
-
1 - filter( NOT EXISTS (SELECT 0 FROM "B" "B" WHERE
-
"B"."OBJECT_NAME"=:B1) OR NOT EXISTS (SELECT 0 FROM "B" "B" WHERE
-
"B"."OBJECT_ID"=:B2))
-
3 - filter("B"."OBJECT_NAME"=:B1)
-
4 - filter("B"."OBJECT_ID"=:B1)
下面开头思路改写。
方法1)一般改写,改写为UNION,这里是not exists,也不能用union all+LNNVL改写,改写了也不能
subquery unnest:
可以改成union,增加rowid防止有重复的
-
select owner,object_name,subobject_name,object_id
-
from (
-
select a.rowid,a.* from a where not exists
-
(select 1 from b where a.object_name = b.object_name)
-
union all
-
select a.rowid,a.* from a where not exists
-
(select 1 from c where a.object_id = c.object_id
-
)
-
);
-
1037 rows selected.
-
-
Elapsed: 00:00:00.09
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 3606514283
-
-
---------------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-
---------------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 3760 | 414K| | 1547 (1)| 00:00:19 |
-
| 1 | VIEW | | 3760 | 414K| | 1547 (1)| 00:00:19 |
-
| 2 | UNION-ALL | | | | | | |
-
|* 3 | HASH JOIN RIGHT ANTI| | 2986 | 183K| 2800K| 933 (1)| 00:00:12 |
-
| 4 | TABLE ACCESS FULL | B | 77370 | 1888K| | 306 (1)| 00:00:04 |
-
| 5 | TABLE ACCESS FULL | A | 77380 | 2871K| | 306 (1)| 00:00:04 |
-
|* 6 | HASH JOIN RIGHT ANTI| | 774 | 33282 | | 614 (1)| 00:00:08 |
-
| 7 | TABLE ACCESS FULL | C | 76640 | 374K| | 306 (1)| 00:00:04 |
-
| 8 | TABLE ACCESS FULL | A | 77380 | 2871K| | 306 (1)| 00:00:04 |
-
---------------------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
-
3 - access("A"."OBJECT_NAME"="B"."OBJECT_NAME")
-
6 - access("A"."OBJECT_ID"="C"."OBJECT_ID")
-
-
-
Statistics
-
----------------------------------------------------------
-
44 recursive calls
-
0 db block gets
-
4475 consistent gets
-
2188 physical reads
-
0 redo size
-
40676 bytes sent via SQL*Net to client
-
1279 bytes received via SQL*Net from client
-
71 SQL*Net roundtrips to/from client
-
4 sorts (memory)
-
0 sorts (disk)
-
1037 rows processed
方法2)集合改写,这个推荐使用(只有子查询表是同一个才可以)
这种是not subq1 or not subq2 ===> not (subq1 and subq2)改写后并不能subquery unnest,因为
两个子查询是不同表,不能直接改到一个子查询里,如下所示:
这种不同表的改写后不能查询转换:
-
select * from a
-
where not
-
(exists
-
(select 1 from b where a.object_name = b.object_name)
-
and exists
-
(select 1 from c where a.object_id = c.object_id)
-
)
-
;
执行计划还是FILTER不能subquery unnest,因为改成了not (subq1 and subq2)形式,不能转换,
能转换的只有not subq1 and not subq2
或not sub1(cond1 and cond2)等格式:
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 2447606385
-
-
----------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
----------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 77378 | 7405K| 224K (1)| 00:45:00 |
-
|* 1 | FILTER | | | | | |
-
| 2 | TABLE ACCESS FULL| A | 77380 | 7405K| 307 (1)| 00:00:04 |
-
|* 3 | INDEX RANGE SCAN | IDX_B | 1 | 25 | 3 (0)| 00:00:01 |
-
|* 4 | TABLE ACCESS FULL| C | 1 | 5 | 306 (1)| 00:00:04 |
-
----------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
-
1 - filter( NOT EXISTS (SELECT 0 FROM "B" "B" WHERE
-
"B"."OBJECT_NAME"=:B1) OR NOT EXISTS (SELECT 0 FROM "C" "C" WHERE
-
"C"."OBJECT_ID"=:B2))
-
3 - access("B"."OBJECT_NAME"=:B1)
-
4 - filter("C"."OBJECT_ID"=:B1)
如果两个子查询的表是同一个,比如下面的两个子查询里都是b表,关联列不同:
-
select * from a
-
where not exists
-
(select 1 from b where a.object_name = b.object_name )
-
or
-
not exists
-
(select 1 from b where a.object_id = b.object_id)
-
;
-
999 rows selected.
-
Elapsed: 00:00:00.25
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 302000962
-
-
-----------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
-----------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 77378 | 7405K| 224K (1)| 00:45:00 |
-
|* 1 | FILTER | | | | | |
-
| 2 | TABLE ACCESS FULL| A | 77380 | 7405K| 307 (1)| 00:00:04 |
-
|* 3 | INDEX RANGE SCAN | IDX_B | 1 | 25 | 3 (0)| 00:00:01 |
-
|* 4 | INDEX RANGE SCAN | IDX1_B | 1 | 5 | 1 (0)| 00:00:01 |
-
-----------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
-
1 - filter( NOT EXISTS (SELECT 0 FROM "B" "B" WHERE
-
"B"."OBJECT_NAME"=:B1) OR NOT EXISTS (SELECT 0 FROM "B" "B" WHERE
-
"B"."OBJECT_ID"=:B2))
-
3 - access("B"."OBJECT_NAME"=:B1)
-
4 - access("B"."OBJECT_ID"=:B1)
-
-
-
Statistics
-
----------------------------------------------------------
-
0 recursive calls
-
0 db block gets
-
231314 consistent gets
-
1094 physical reads
-
0 redo size
-
52714 bytes sent via SQL*Net to client
-
1246 bytes received via SQL*Net from client
-
68 SQL*Net roundtrips to/from client
-
0 sorts (memory)
-
0 sorts (disk)
-
999 rows processed
这种可以利用集合转换,not cond1 or cond2 => not (cond1 and cond2),比如下面的,改写后走
HASH JOIN:
-
select * from a
-
where not exists
-
(select 1 from b where a.object_name = b.object_name and a.object_id = b.object_id)
-
;
-
-
999 rows selected.
执行计划显示可以查询转换,无FILTER,走HASH JOIN:
-
Elapsed: 00:00:00.07
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 2798188842
-
-
-------------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-
-------------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 774 | 99072 | | 1172 (1)| 00:00:15 |
-
|* 1 | HASH JOIN RIGHT ANTI| | 774 | 99072 | 3176K| 1172 (1)| 00:00:15 |
-
| 2 | TABLE ACCESS FULL | B | 77370 | 2266K| | 306 (1)| 00:00:04 |
-
| 3 | TABLE ACCESS FULL | A | 77380 | 7405K| | 307 (1)| 00:00:04 |
-
-------------------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
-
1 - access("A"."OBJECT_NAME"="B"."OBJECT_NAME" AND
-
"A"."OBJECT_ID"="B"."OBJECT_ID")
-
-
-
Statistics
-
----------------------------------------------------------
-
0 recursive calls
-
0 db block gets
-
2258 consistent gets
-
2188 physical reads
-
0 redo size
-
52714 bytes sent via SQL*Net to client
-
1246 bytes received via SQL*Net from client
-
68 SQL*Net roundtrips to/from client
-
0 sorts (memory)
-
0 sorts (disk)
-
999 rows processed
SQL2:这种not exists(xx1 or xx2),意思是只有都不满足xx1或xx2条件的才返回(因为是anti
join),和前面的not subq1 or not subq2不一样(前面的可以改成union )
这种如果是anti join,不能简单改成union 了,不等价,看起来应该改成intersect),改写
使用集合运算,改写为not exists subq1 and not exists subq2
如下例:
-
select * from a
-
where not exists
-
(select 1 from b where a.object_name = b.object_name or a.object_id=b.object_id);
执行计划显示无法unnest,有FILTER:
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 1049914119
-
-
---------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
---------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 77380 | 7405K| 8169K (1)| 27:13:49 |
-
|* 1 | FILTER | | | | | |
-
| 2 | TABLE ACCESS FULL| A | 77380 | 7405K| 307 (1)| 00:00:04 |
-
|* 3 | TABLE ACCESS FULL| B | 2 | 60 | 109 (1)| 00:00:02 |
-
---------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
-
1 - filter( NOT EXISTS (SELECT 0 FROM "B" "B" WHERE
-
"B"."OBJECT_NAME"=:B1 OR "B"."OBJECT_ID"=:B2))
-
3 - filter("B"."OBJECT_NAME"=:B1 OR "B"."OBJECT_ID"=:B2)
非常慢,可以对b表object_name,object_id分别建立索引,这样走or扩展,走索引好点。
create index idx_b on b(object_name);
create index idx1_b on b(object_id);
执行计划走FILTER,子查询因为是不同列,走INDEX OR转换,转为BITMAP INDEX,执行计划如
下:
-
Elapsed: 00:00:00.43
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 877580492
-
-
-------------------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
-------------------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 77380 | 7405K| 337K (1)| 01:07:32 |
-
|* 1 | FILTER | | | | | |
-
| 2 | TABLE ACCESS FULL | A | 77380 | 7405K| 307 (1)| 00:00:04 |
-
| 3 | BITMAP CONVERSION TO ROWIDS | | | | | |
-
| 4 | BITMAP OR | | | | | |
-
| 5 | BITMAP CONVERSION FROM ROWIDS| | | | | |
-
|* 6 | INDEX RANGE SCAN | IDX_B | | | 3 (0)| 00:00:01 |
-
| 7 | BITMAP CONVERSION FROM ROWIDS| | | | | |
-
|* 8 | INDEX RANGE SCAN | IDX1_B | | | 1 (0)| 00:00:01 |
-
-------------------------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
-
1 - filter( NOT EXISTS (SELECT 0 FROM "B" "B" WHERE "B"."OBJECT_NAME"=:B1 OR
-
"B"."OBJECT_ID"=:B2))
-
6 - access("B"."OBJECT_NAME"=:B1)
-
8 - access("B"."OBJECT_ID"=:B1)
-
-
-
Statistics
-
----------------------------------------------------------
-
1 recursive calls
-
0 db block gets
-
140193 consistent gets
-
1648 physical reads
-
0 redo size
-
51946 bytes sent via SQL*Net to client
-
1235 bytes received via SQL*Net from client
-
67 SQL*Net roundtrips to/from client
-
0 sorts (memory)
-
0 sorts (disk)
-
988 rows processed
分析与优化:
方法1):子查询有or条件,对应主表的列不是同一个列,不能改写为UNION ALL,只能走
FILTER,需要手动改写。
对于这种可以用集合化运算进行转换,NOT (A OR B) ==> NOT A AND NOT B,则可以对
subquery进行unnest转换提高效率.
可以改写为:
-
select * from a
-
where not exists
-
(select 1 from b where a.object_name = b.object_name)
-
and not exists
-
(
-
select 1 from b where a.object_id=b.object_id
-
);
执行计划没有FILTER了,走HASH JOIN,变为0.06s,如下所示:
-
988 rows selected.
-
-
Elapsed: 00:00:00.06
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 3793835392
-
-
------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 8 | 1024 | 921 (1)| 00:00:12 |
-
|* 1 | HASH JOIN ANTI | | 8 | 1024 | 921 (1)| 00:00:12 |
-
|* 2 | HASH JOIN RIGHT ANTI| | 774 | 79722 | 615 (1)| 00:00:08 |
-
| 3 | TABLE ACCESS FULL | B | 77370 | 377K| 306 (1)| 00:00:04 |
-
| 4 | TABLE ACCESS FULL | A | 77380 | 7405K| 307 (1)| 00:00:04 |
-
| 5 | TABLE ACCESS FULL | B | 77370 | 1888K| 306 (1)| 00:00:04 |
-
------------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
-
1 - access("A"."OBJECT_NAME"="B"."OBJECT_NAME")
-
2 - access("A"."OBJECT_ID"="B"."OBJECT_ID")
-
-
-
Statistics
-
----------------------------------------------------------
-
1 recursive calls
-
0 db block gets
-
3292 consistent gets
-
1094 physical reads
-
0 redo size
-
81836 bytes sent via SQL*Net to client
-
1235 bytes received via SQL*Net from client
-
67 SQL*Net roundtrips to/from client
-
0 sorts (memory)
-
0 sorts (disk)
-
988 rows processed
方法2):not exists(xx1 or xx2)这种不能简单改成union all,不等价,因为是anti join,实际上返回的
行必须or条件都找不到,所以可以改成intersect:
-
select owner,object_name,subobject_name,object_id
-
from (
-
select a.rowid,a.* from a where not exists
-
(select 1 from b where a.object_name = b.object_name)
-
intersect
-
select a.rowid,a.* from a where not exists
-
(select 1 from b where a.object_id = b.object_id
-
)
-
);
-
988 rows selected.
-
-
Elapsed: 00:00:00.08
执行计划如下:
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 14435923
-
-
----------------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-
----------------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 774 | 87462 | | 1771 (1)| 00:00:22 |
-
| 1 | VIEW | | 774 | 87462 | | 1771 (1)| 00:00:22 |
-
| 2 | INTERSECTION | | | | | | |
-
| 3 | SORT UNIQUE | | 2986 | 358K| | 1155 (1)| 00:00:14 |
-
|* 4 | HASH JOIN RIGHT ANTI| | 2986 | 358K| 2800K| 1154 (1)| 00:00:14 |
-
| 5 | TABLE ACCESS FULL | B | 77370 | 1888K| | 306 (1)| 00:00:04 |
-
| 6 | TABLE ACCESS FULL | A | 77380 | 7405K| | 307 (1)| 00:00:04 |
-
| 7 | SORT UNIQUE | | 774 | 79722 | | 616 (1)| 00:00:08 |
-
|* 8 | HASH JOIN RIGHT ANTI| | 774 | 79722 | | 615 (1)| 00:00:08 |
-
| 9 | TABLE ACCESS FULL | B | 77370 | 377K| | 306 (1)| 00:00:04 |
-
| 10 | TABLE ACCESS FULL | A | 77380 | 7405K| | 307 (1)| 00:00:04 |
-
----------------------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
-
4 - access("A"."OBJECT_NAME"="B"."OBJECT_NAME")
-
8 - access("A"."OBJECT_ID"="B"."OBJECT_ID")
-
-
-
Statistics
-
----------------------------------------------------------
-
1 recursive calls
-
0 db block gets
-
4388 consistent gets
-
2188 physical reads
-
0 redo size
-
38197 bytes sent via SQL*Net to client
-
1235 bytes received via SQL*Net from client
-
67 SQL*Net roundtrips to/from client
-
2 sorts (memory)
-
0 sorts (disk)
-
988 rows processed
2.SEMI JOIN OR SUBQUERY 改写
前面例子对于exp or subq改为UNION ALL,使用LNNVL,下面的要复杂点,不是简单表达式,是
子查询。
1) exists subq1 or exists subq2 含义是只要满足一个即可,可以改为union+rowid、union all+
一个subq取反、union all+lnnvl(子查询分支表是同一个)
-
select * from a
-
where exists(select 1 from b where a.object_name = b.object_name)
-
or exists (select 1 from c where a.object_id = c.object_id);
-
-
76834 rows selected.
-
-
Elapsed: 00:00:01.32
执行计划出现FILTER:
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 2447606385
-
-
----------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
----------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 2 | 196 | 224K (1)| 00:45:00 |
-
|* 1 | FILTER | | | | | |
-
| 2 | TABLE ACCESS FULL| A | 77380 | 7405K| 307 (1)| 00:00:04 |
-
|* 3 | INDEX RANGE SCAN | IDX_B | 1 | 25 | 3 (0)| 00:00:01 |
-
|* 4 | TABLE ACCESS FULL| C | 1 | 5 | 306 (1)| 00:00:04 |
-
----------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
-
1 - filter( EXISTS (SELECT 0 FROM "B" "B" WHERE
-
"B"."OBJECT_NAME"=:B1) OR EXISTS (SELECT 0 FROM "C" "C" WHERE
-
"C"."OBJECT_ID"=:B2))
-
3 - access("B"."OBJECT_NAME"=:B1)
-
4 - filter("C"."OBJECT_ID"=:B1)
-
-
-
Statistics
-
----------------------------------------------------------
-
1 recursive calls
-
0 db block gets
-
169738 consistent gets
-
1094 physical reads
-
0 redo size
-
4013099 bytes sent via SQL*Net to client
-
56862 bytes received via SQL*Net from client
-
5124 SQL*Net roundtrips to/from client
-
0 sorts (memory)
-
0 sorts (disk)
-
76834 rows processed
下面研究下semi join or subquery改写:
方法1:改为union ,需要剔除重复行,可以用rowid
-
select owner,object_name,subobject_name,object_id
-
from(
-
select a.rowid,a.* from a
-
where exists(select 1 from b where a.object_name = b.object_name)
-
union
-
select a.rowid,a.* from a
-
where exists (select 1 from c where a.object_id = c.object_id)
-
);
-
-
76834 rows selected.
-
-
Elapsed: 00:00:00.87
可以sunquery unnest,如下所示:
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 1836858707
-
-
------------------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-
------------------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 151K| 16M| | 5424 (1)| 00:01:06 |
-
| 1 | VIEW | | 151K| 16M| | 5424 (1)| 00:01:06 |
-
| 2 | SORT UNIQUE | | 151K| 16M| 17M| 5424 (45)| 00:01:06 |
-
| 3 | UNION-ALL | | | | | | |
-
|* 4 | HASH JOIN RIGHT SEMI | | 74394 | 8935K| 2800K| 952 (1)| 00:00:12 |
-
| 5 | INDEX FAST FULL SCAN| IDX_B | 77370 | 1888K| | 104 (1)| 00:00:02 |
-
| 6 | TABLE ACCESS FULL | A | 77380 | 7405K| | 307 (1)| 00:00:04 |
-
|* 7 | HASH JOIN RIGHT SEMI | | 76640 | 7708K| | 615 (1)| 00:00:08 |
-
| 8 | TABLE ACCESS FULL | C | 76640 | 374K| | 306 (1)| 00:00:04 |
-
| 9 | TABLE ACCESS FULL | A | 77380 | 7405K| | 307 (1)| 00:00:04 |
-
------------------------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
-
4 - access("A"."OBJECT_NAME"="B"."OBJECT_NAME")
-
7 - access("A"."OBJECT_ID"="C"."OBJECT_ID")
-
-
-
Statistics
-
----------------------------------------------------------
-
1 recursive calls
-
0 db block gets
-
3675 consistent gets
-
2188 physical reads
-
0 redo size
-
3255132 bytes sent via SQL*Net to client
-
56862 bytes received via SQL*Net from client
-
5124 SQL*Net roundtrips to/from client
-
1 sorts (memory)
-
0 sorts (disk)
-
76834 rows processed
方法2:因为这里两个分支是不同表,所以改为union all+一个分支取反(not exists)
-
select owner,object_name,subobject_name,object_id
-
from(
-
select a.rowid,a.* from a
-
where exists(select 1 from b where a.object_name = b.object_name)
-
union all
-
select a.rowid,a.* from a
-
where exists (select 1 from c where a.object_id = c.object_id)
-
and not exists
-
(select 1 from b where a.object_name = b.object_name)
-
);
-
-
76834 rows selected.
-
-
Elapsed: 00:00:00.79
执行计划如下:
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 907581613
-
-
------------------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-
------------------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 77379 | 8538K| | 1768 (1)| 00:00:22 |
-
| 1 | VIEW | | 77379 | 8538K| | 1768 (1)| 00:00:22 |
-
| 2 | UNION-ALL | | | | | | |
-
|* 3 | HASH JOIN RIGHT SEMI | | 74394 | 4576K| 2800K| 731 (1)| 00:00:09 |
-
| 4 | INDEX FAST FULL SCAN | IDX_B | 77370 | 1888K| | 104 (1)| 00:00:02 |
-
| 5 | TABLE ACCESS FULL | A | 77380 | 2871K| | 306 (1)| 00:00:04 |
-
|* 6 | HASH JOIN SEMI | | 2985 | 198K| | 1038 (1)| 00:00:13 |
-
|* 7 | HASH JOIN RIGHT ANTI | | 2986 | 183K| 2800K| 731 (1)| 00:00:09 |
-
| 8 | INDEX FAST FULL SCAN| IDX_B | 77370 | 1888K| | 104 (1)| 00:00:02 |
-
| 9 | TABLE ACCESS FULL | A | 77380 | 2871K| | 306 (1)| 00:00:04 |
-
| 10 | TABLE ACCESS FULL | C | 76640 | 374K| | 306 (1)| 00:00:04 |
-
------------------------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
-
3 - access("A"."OBJECT_NAME"="B"."OBJECT_NAME")
-
6 - access("A"."OBJECT_ID"="C"."OBJECT_ID")
-
7 - access("A"."OBJECT_NAME"="B"."OBJECT_NAME")
-
-
-
Statistics
-
----------------------------------------------------------
-
0 recursive calls
-
0 db block gets
-
9109 consistent gets
-
2188 physical reads
-
0 redo size
-
3255141 bytes sent via SQL*Net to client
-
56862 bytes received via SQL*Net from client
-
5124 SQL*Net roundtrips to/from client
-
0 sorts (memory)
-
0 sorts (disk)
-
76834 rows processed
方法3 如果or子查询来源于同一个表,semi join改成union all+lnnvl
如果两个分支是来源于同一个表,则可以使用union all+lnnvl,如下所示,下面子查询都是b表:
-
select * from a
-
where exists(select 1 from b where a.object_name = b.object_name)
-
or exists (select 1 from b where a.object_id = b.object_id);
-
75848 rows selected.
-
-
Elapsed: 00:00:01.59
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 877580492
-
-
-------------------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
-------------------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 1 | 98 | 337K (1)| 01:07:32 |
-
|* 1 | FILTER | | | | | |
-
| 2 | TABLE ACCESS FULL | A | 77380 | 7405K| 307 (1)| 00:00:04 |
-
| 3 | BITMAP CONVERSION TO ROWIDS | | | | | |
-
| 4 | BITMAP OR | | | | | |
-
| 5 | BITMAP CONVERSION FROM ROWIDS| | | | | |
-
|* 6 | INDEX RANGE SCAN | IDX_B | | | 3 (0)| 00:00:01 |
-
| 7 | BITMAP CONVERSION FROM ROWIDS| | | | | |
-
|* 8 | INDEX RANGE SCAN | IDX1_B | | | 1 (0)| 00:00:01 |
-
-------------------------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
-
1 - filter( EXISTS (SELECT 0 FROM "B" "B" WHERE "B"."OBJECT_NAME"=:B1 OR
-
"B"."OBJECT_ID"=:B2))
-
6 - access("B"."OBJECT_NAME"=:B1)
-
8 - access("B"."OBJECT_ID"=:B1)
-
-
-
Statistics
-
----------------------------------------------------------
-
1 recursive calls
-
0 db block gets
-
155671 consistent gets
-
1094 physical reads
-
0 redo size
-
3962659 bytes sent via SQL*Net to client
-
56136 bytes received via SQL*Net from client
-
5058 SQL*Net roundtrips to/from client
-
0 sorts (memory)
-
0 sorts (disk)
-
75848 rows processed
改为UNION ALL+LNNVL,这种改写带LNNVL的不能subquery unnest:
-
select owner,object_name,subobject_name,object_id
-
from(
-
select a.rowid,a.* from a
-
where exists(select 1 from b where a.object_name = b.object_name)
-
union all
-
select a.rowid,a.* from a
-
where exists (select 1 from b where a.object_id = b.object_id and lnnvl(a.object_name = b.object_name))
-
);
-
-
-
75848 rows selected.
-
-
Elapsed: 00:00:00.92
执行计划貌似对于LNNVL的还是FILTER,这里的LNNVL是关联条件,前面的是简单表达式可以:
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 2137227284
-
-
-------------------------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-
-------------------------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 74395 | 8209K| | 151K (1)| 00:30:20 |
-
| 1 | VIEW | | 74395 | 8209K| | 151K (1)| 00:30:20 |
-
| 2 | UNION-ALL | | | | | | |
-
|* 3 | HASH JOIN RIGHT SEMI | | 74394 | 4576K| 2800K| 731 (1)| 00:00:09 |
-
| 4 | INDEX FAST FULL SCAN | IDX_B | 77370 | 1888K| | 104 (1)| 00:00:02 |
-
| 5 | TABLE ACCESS FULL | A | 77380 | 2871K| | 306 (1)| 00:00:04 |
-
|* 6 | FILTER | | | | | | |
-
| 7 | TABLE ACCESS FULL | A | 77380 | 2871K| | 306 (1)| 00:00:04 |
-
|* 8 | TABLE ACCESS BY INDEX ROWID| B | 1 | 30 | | 2 (0)| 00:00:01 |
-
|* 9 | INDEX RANGE SCAN | IDX1_B | 1 | | | 1 (0)| 00:00:01 |
-
-------------------------------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
-
3 - access("A"."OBJECT_NAME"="B"."OBJECT_NAME")
-
6 - filter( EXISTS (SELECT 0 FROM "B" "B" WHERE "B"."OBJECT_ID"=:B1 AND
-
LNNVL("B"."OBJECT_NAME"=:B2)))
-
8 - filter(LNNVL("B"."OBJECT_NAME"=:B1))
-
9 - access("B"."OBJECT_ID"=:B1)
-
-
-
Statistics
-
----------------------------------------------------------
-
1 recursive calls
-
0 db block gets
-
161911 consistent gets
-
2188 physical reads
-
0 redo size
-
3217600 bytes sent via SQL*Net to client
-
56136 bytes received via SQL*Net from client
-
5058 SQL*Net roundtrips to/from client
-
0 sorts (memory)
-
0 sorts (disk)
-
75848 rows processed
当然,用前面的方法改为not exists是可以的:
-
select owner,object_name,subobject_name,object_id
-
from(
-
select a.rowid,a.* from a
-
where exists(select 1 from b where a.object_name = b.object_name)
-
union all
-
select a.rowid,a.* from a
-
where exists (select 1 from b where a.object_id = b.object_id)
-
and not exists(select 1 from b where a.object_name = b.object_name)
-
);
-
-
Elapsed: 00:00:00.78
执行计划如下:
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 434197087
-
-
-------------------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-
-------------------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 77379 | 8538K| | 1510 (1)| 00:00:19 |
-
| 1 | VIEW | | 77379 | 8538K| | 1510 (1)| 00:00:19 |
-
| 2 | UNION-ALL | | | | | | |
-
|* 3 | HASH JOIN RIGHT SEMI | | 74394 | 4576K| 2800K| 731 (1)| 00:00:09 |
-
| 4 | INDEX FAST FULL SCAN | IDX_B | 77370 | 1888K| | 104 (1)| 00:00:02 |
-
| 5 | TABLE ACCESS FULL | A | 77380 | 2871K| | 306 (1)| 00:00:04 |
-
|* 6 | HASH JOIN SEMI | | 2985 | 198K| | 779 (1)| 00:00:10 |
-
|* 7 | HASH JOIN RIGHT ANTI | | 2986 | 183K| 2800K| 731 (1)| 00:00:09 |
-
| 8 | INDEX FAST FULL SCAN| IDX_B | 77370 | 1888K| | 104 (1)| 00:00:02 |
-
| 9 | TABLE ACCESS FULL | A | 77380 | 2871K| | 306 (1)| 00:00:04 |
-
| 10 | INDEX FAST FULL SCAN | IDX1_B | 77370 | 377K| | 47 (0)| 00:00:01 |
-
-------------------------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
-
3 - access("A"."OBJECT_NAME"="B"."OBJECT_NAME")
-
6 - access("A"."OBJECT_ID"="B"."OBJECT_ID")
-
7 - access("A"."OBJECT_NAME"="B"."OBJECT_NAME")
-
-
-
Statistics
-
----------------------------------------------------------
-
0 recursive calls
-
0 db block gets
-
8121 consistent gets
-
2188 physical reads
-
0 redo size
-
3217600 bytes sent via SQL*Net to client
-
56136 bytes received via SQL*Net from client
-
5058 SQL*Net roundtrips to/from client
-
0 sorts (memory)
-
0 sorts (disk)
-
75848 rows processed
lnnvl(a.object_name = b.object_name)等价于
a.object_name<>b.object_name or a.object_name is null or b.object_name is null
在老虎刘那学了一招,可以通过case when改写:
case when a.object_name<>b.object_name or a.object_name is null or b.object_name is null
then 0 end = 0
-
select * from a
-
where exists
-
(select 1 from b where a.object_name = b.object_name)
-
union all
-
select * from a
-
where exists
-
(select 1 from b where a.object_id = b.object_id
-
and case when a.object_name<>b.object_name or a.object_name is null or b.object_name is null then 0 end = 0);
-
-
75848 rows selected.
效率上看,还没有改成not exists的好(和具体数据分布有关),因为建了索引,直接lnnvl走FILTER
的也不错。
-
Elapsed: 00:00:01.17
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 884194011
-
-
----------------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-
----------------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 75168 | 9032K| | 2124 (56)| 00:00:26 |
-
| 1 | UNION-ALL | | | | | | |
-
|* 2 | HASH JOIN RIGHT SEMI | | 74394 | 8935K| 2800K| 952 (1)| 00:00:12 |
-
| 3 | INDEX FAST FULL SCAN| IDX_B | 77370 | 1888K| | 104 (1)| 00:00:02 |
-
| 4 | TABLE ACCESS FULL | A | 77380 | 7405K| | 307 (1)| 00:00:04 |
-
|* 5 | HASH JOIN RIGHT SEMI | | 774 | 99072 | 3176K| 1172 (1)| 00:00:15 |
-
| 6 | TABLE ACCESS FULL | B | 77370 | 2266K| | 306 (1)| 00:00:04 |
-
| 7 | TABLE ACCESS FULL | A | 77380 | 7405K| | 307 (1)| 00:00:04 |
-
----------------------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
-
2 - access("A"."OBJECT_NAME"="B"."OBJECT_NAME")
-
5 - access("A"."OBJECT_ID"="B"."OBJECT_ID")
-
filter(CASE WHEN ("A"."OBJECT_NAME"<>"B"."OBJECT_NAME" OR
-
"A"."OBJECT_NAME" IS NULL OR "B"."OBJECT_NAME" IS NULL) THEN 0 END =0)
-
-
-
Statistics
-
----------------------------------------------------------
-
0 recursive calls
-
0 db block gets
-
8659 consistent gets
-
2188 physical reads
-
0 redo size
-
3962659 bytes sent via SQL*Net to client
-
56136 bytes received via SQL*Net from client
-
5058 SQL*Net roundtrips to/from client
-
0 sorts (memory)
-
0 sorts (disk)
-
75848 rows processed
对于not exists用上面的case when改貌似不行,exists可以,anti join不可以。
子查询转换挺复杂的,特别是带OR的子查询,经常性无法查询转换走FILTER,造成性能低下,了解
一些OR子查询转换和改写规则,有助于提升子查询性能,理解优化器的查询转换。
此外,子查询转换还有很多限制,比如子查询里有rownum等复杂的条件,也可能阻止unnest。
阅读(1815) | 评论(0) | 转发(0) |