非相关的希望单独执行一次即可,然后结果缓存下来,对于存在性判断的非相关子查询,希望只执行一次只查询一条。
pgsql的select * from a where exists(select 1 from b where a.id=b.id);这种是判断a.id is not null+子表b有数据则执行的逻辑,
pg这种判断比oracle,mysql要弱点,它可能走nested loops semi+物化,循环驱动物化表多次,虽然物化表{BANNED}最佳多只存一行(因为存在性判断),
oracle走FILTER没有问题,MySQL走hash join semi也没有问题,子表b只执行一次只查一行,也{BANNED}最佳多被驱动一次。
==================Oracle
这是完全存在性判断,子查询只要有一条,就执行主表条件,子查询没有结果就不执行。这种完全存在性判断,就算子查询走全表也没有关系,
union all的2个分支,只要找到一条即可,而且子查询只执行一次。
select * from a1 where exists(select 1 from b1 union all select 1 from b2);
Elapsed: 00:00:00.00
执行计划走FILTER,按照FILTER逻辑是主表的每一行驱动子查询(可能有CACHE减少驱动次数),如果这样理解,那么主表有99行,子查询应该驱动执行99次,而且
全表扫描,那么效率必然很低,实际上呢?
见ID=1的FILTER谓词: 1 - filter( EXISTS ( (SELECT 1 FROM "B1" "B1") UNION ALL (SELECT 1 FROM "B2" "B2")))
这里不像一般的FILTER是有系统绑定变量:B1的,所以,这个FILTER实际上是个存在性判断,O这个计划显示的容易误导人,应该是
子查询找1条,驱动主表,这样就容易理解了
点击(此处)折叠或打开
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 1160945571
-
-
----------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
----------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 99 | 10494 | 9 (0)| 00:00:01 |
-
|* 1 | FILTER | | | | | |####实际上是假的FILTER
-
| 2 | TABLE ACCESS FULL | A1 | 99 | 10494 | 3 (0)| 00:00:01 |
-
| 3 | UNION-ALL | | | | | |
-
| 4 | TABLE ACCESS FULL| B1 | 99 | | 3 (0)| 00:00:01 |
-
| 5 | TABLE ACCESS FULL| B2 | 99 | | 3 (0)| 00:00:01 |
-
----------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
-
1 - filter( EXISTS ( (SELECT 1 FROM "B1" "B1") UNION ALL (SELECT 1
-
FROM "B2" "B2")))
-
-
-
Statistics
-
----------------------------------------------------------
-
0 recursive calls
-
0 db block gets
-
14 consistent gets
-
0 physical reads
-
0 redo size
-
8764 bytes sent via SQL*Net to client
-
503 bytes received via SQL*Net from client
-
8 SQL*Net roundtrips to/from client
-
0 sorts (memory)
-
0 sorts (disk)
- 99 rows processed
可以通过真实执行计划看到,子查询Starts{BANNED}最佳多是1,而且B1,B2只找了B1,而且只找到一条,则直接查询A1。所以虽然是FILTER,但是实际执行逻辑
是完全存在性判断,是先执行子查询判断,再执行主表的。
点击(此处)折叠或打开
-
select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
-
-
Plan hash value: 1160945571
-
-
--------------------------------------------------------------------------------------
-
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-
--------------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 1 | | 99 |00:00:00.01 | 14 |
-
|* 1 | FILTER | | 1 | | 99 |00:00:00.01 | 14 |
-
| 2 | TABLE ACCESS FULL | A1 | 1 | 99 | 99 |00:00:00.01 | 11 |
-
| 3 | UNION-ALL | | 1 | | 1 |00:00:00.01 | 3 |
-
| 4 | TABLE ACCESS FULL| B1 | 1 | 99 | 1 |00:00:00.01 | 3 |
-
| 5 | TABLE ACCESS FULL| B2 | 0 | 99 | 0 |00:00:00.01 | 0 |
-
--------------------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
- 1 - filter( IS NOT NULL)
实际的逻辑应该是这样的,B2的starts=0,因为B1找到了1条:
点击(此处)折叠或打开
-
select a1.* from a1,(select 1 from (select 1 from b1 union all select 1 from b2) where rownum=1);
-
-
-
99 rows selected.
-
-
Elapsed: 00:00:00.00
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 822935806
-
-
-------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
-------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 99 | 10494 | 9 (0)| 00:00:01 |
-
| 1 | MERGE JOIN CARTESIAN | | 99 | 10494 | 9 (0)| 00:00:01 |
-
| 2 | VIEW | | 1 | | 6 (0)| 00:00:01 |
-
|* 3 | COUNT STOPKEY | | | | | |
-
| 4 | VIEW | | 198 | | 6 (0)| 00:00:01 |
-
| 5 | UNION-ALL | | | | | |
-
| 6 | TABLE ACCESS FULL| B1 | 99 | | 3 (0)| 00:00:01 |
-
| 7 | TABLE ACCESS FULL| B2 | 99 | | 3 (0)| 00:00:01 |
-
| 8 | BUFFER SORT | | 99 | 10494 | 9 (0)| 00:00:01 |
-
| 9 | TABLE ACCESS FULL | A1 | 99 | 10494 | 3 (0)| 00:00:01 |
-
-------------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
-
3 - filter(ROWNUM=1)
-
-
-
-
PLAN_TABLE_OUTPUT
-
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-
SQL_ID gqacy9u017x5g, child number 0
-
-------------------------------------
-
select a1.* from a1,(select 1 from (select 1 from b1 union all
-
select 1 from b2) where rownum=1)
-
-
Plan hash value: 822935806
-
-
--------------------------------------------------------------------------------------------------------------------
-
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem |
-
--------------------------------------------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 1 | | 99 |00:00:00.01 | 7 | | | |
-
| 1 | MERGE JOIN CARTESIAN | | 1 | 99 | 99 |00:00:00.01 | 7 | | | |
-
| 2 | VIEW | | 1 | 1 | 1 |00:00:00.01 | 3 | | | |
-
|* 3 | COUNT STOPKEY | | 1 | | 1 |00:00:00.01 | 3 | | | |
-
| 4 | VIEW | | 1 | 198 | 1 |00:00:00.01 | 3 | | | |
-
| 5 | UNION-ALL | | 1 | | 1 |00:00:00.01 | 3 | | | |
-
| 6 | TABLE ACCESS FULL| B1 | 1 | 99 | 1 |00:00:00.01 | 3 | | | |
-
| 7 | TABLE ACCESS FULL| B2 | 0 | 99 | 0 |00:00:00.01 | 0 | | | |
-
| 8 | BUFFER SORT | | 1 | 99 | 99 |00:00:00.01 | 4 | 20480 | 20480 |18432 (0)|
-
| 9 | TABLE ACCESS FULL | A1 | 1 | 99 | 99 |00:00:00.01 | 4 | | | |
-
--------------------------------------------------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
- 3 - filter(ROWNUM=1)
这种存在性判断,子查询条件where a1.object_id=a1.object_id (一样的),只找a1的object_id is not null + 表b1有数据即可,
使用FILTER存在性检查,同样这种a1和b1没有关联的,对于子查询b1只找1次,1条即可。
oracle这种还是比较智能的,后面可以看看pg,mysql的,会转为nested loops semi,会执行多次b1的扫描,不够优化,当然他们会使用materilization技术提高效率。
点击(此处)折叠或打开
-
select * from a1 where exists (select 1 from b1 where a1.object_id=a1.object_id);
-
select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
-
-
PLAN_TABLE_OUTPUT
-
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-
SQL_ID 4snt03yzrpmcq, child number 0
-
-------------------------------------
-
select * from a1 where exists (select 1 from b1 where
-
a1.object_id=a1.object_id)
-
-
Plan hash value: 1803926644
-
-
-------------------------------------------------------------------------------------
-
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-
-------------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 1 | | 99 |00:00:00.01 | 13 |
-
|* 1 | FILTER | | 1 | | 99 |00:00:00.01 | 13 |
-
|* 2 | TABLE ACCESS FULL| A1 | 1 | 99 | 99 |00:00:00.01 | 11 |
-
| 3 | TABLE ACCESS FULL| B1 | 1 | 1 | 1 |00:00:00.01 | 2 |
-
-------------------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
-
1 - filter( IS NOT NULL)
- 2 - filter("A1"."OBJECT_ID" IS NOT NULL
等价于这种写法,执行计划与上面的完全一样,像pg改成这种才能扫描b1一次。
点击(此处)折叠或打开
-
select * from a1
-
where exists (select 1 from b1)
-
and a1.object_id is not null;
-
-
PLAN_TABLE_OUTPUT
-
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-
SQL_ID cwt341hjrrppu, child number 0
-
-------------------------------------
-
select * from a1 where exists (select 1 from b1) and a1.object_id is
-
not null
-
-
Plan hash value: 1803926644
-
-
-------------------------------------------------------------------------------------
-
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-
-------------------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 1 | | 99 |00:00:00.01 | 13 |
-
|* 1 | FILTER | | 1 | | 99 |00:00:00.01 | 13 |
-
|* 2 | TABLE ACCESS FULL| A1 | 1 | 99 | 99 |00:00:00.01 | 11 |
-
| 3 | TABLE ACCESS FULL| B1 | 1 | 1 | 1 |00:00:00.01 | 2 |
-
-------------------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
-
1 - filter( IS NOT NULL)
- 2 - filter("A1"."OBJECT_ID" IS NOT NULL)