我在执行以下查询 ( Q1
) 时遇到性能问题:
select
z_out.*,
a_out.id
from orders a_out, test z_out
where a_out.id=z_out.id and a_out.created>trunc(sysdate) and rownum<10
表orders
包含数百万行;orders.id
是主键并被orders.craeted
索引。
观点是:
create or replace view test as
select/*+qb_name(q_outer)*/
id,
min(value) keep (dense_rank first order by id) as value
from (
select/*+qb_name(q_inner)*/
id,
case
when substr(id, -1)<'5'
--and exists(select 1 from dual@db2)
then 'YYY'
end as attr_1
from orders a1
) a2, small_table b2
where b2.attr_1 in (nvl(a2.attr_1, '#'), '*')
group by id
其中small_table b2
包含大约 200 条记录(所有列都是varchar2
)。
ExecutingQ1
有很好的性能和以下执行计划:
Plan hash value: 2906430222
-----------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
-----------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 274 | 64 (0)| 00:00:01 | | |
|* 1 | COUNT STOPKEY | | | | | | | |
| 2 | NESTED LOOPS | | 1 | 274 | 64 (0)| 00:00:01 | | |
| 3 | PARTITION LIST ALL | | 1 | 22 | 59 (0)| 00:00:01 | 1 | 2 |
| 4 | PARTITION RANGE ALL | | 1 | 22 | 59 (0)| 00:00:01 | 1 | LAST |
| 5 | TABLE ACCESS BY LOCAL INDEX ROWID| ORDERS | 1 | 22 | 59 (0)| 00:00:01 | 1 | 29 |
|* 6 | INDEX RANGE SCAN | IDX_ORDERS_CREATED | 1 | | 57 (0)| 00:00:01 | 1 | 29 |
| 7 | VIEW PUSHED PREDICATE | TEST | 1 | 252 | 5 (0)| 00:00:01 | | |
|* 8 | FILTER | | | | | | | |
| 9 | SORT AGGREGATE | | 1 | 55 | | | | |
| 10 | NESTED LOOPS | | 259 | 14245 | 5 (0)| 00:00:01 | | |
|* 11 | INDEX UNIQUE SCAN | PK_ID | 1 | 14 | 2 (0)| 00:00:01 | | |
|* 12 | INDEX STORAGE FAST FULL SCAN | IDX_MN_AN_AD_ALL | 259 | 10619 | 3 (0)| 00:00:01 | | |
-----------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(ROWNUM<10)
6 - access("A_OUT"."CREATED">TRUNC(SYSDATE@!))
8 - filter(COUNT(*)>0)
11 - access("ID"="A_OUT"."ID")
12 - storage("B2"."ATTR_1"=NVL(CASE WHEN SUBSTR("ID",(-1))<'5' THEN 'YYY' END ,'#') OR "B2"."ATTR_1"='*')
filter("B2"."ATTR_1"=NVL(CASE WHEN SUBSTR("ID",(-1))<'5' THEN 'YYY' END ,'#') OR "B2"."ATTR_1"='*')
Q1
--and exists(select 1 from dual@db2)
取消注释视图中的行时会发生性能问题。新的执行计划是:
Plan hash value: 3271081243
----------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop | Inst |IN-OUT|
----------------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 288 | 5273K (1)| 00:03:27 | | | | |
|* 1 | COUNT STOPKEY | | | | | | | | | |
|* 2 | HASH JOIN | | 1 | 288 | 5273K (1)| 00:03:27 | | | | |
| 3 | JOIN FILTER CREATE | :BF0000 | 1 | 22 | 59 (0)| 00:00:01 | | | | |
| 4 | PARTITION LIST ALL | | 1 | 22 | 59 (0)| 00:00:01 | 1 | 2 | | |
| 5 | PARTITION RANGE ALL | | 1 | 22 | 59 (0)| 00:00:01 | 1 | LAST | | |
| 6 | TABLE ACCESS BY LOCAL INDEX ROWID| ORDERS | 1 | 22 | 59 (0)| 00:00:01 | 1 | 29 | | |
|* 7 | INDEX RANGE SCAN | IDX_ORDERS_CREATED | 1 | | 57 (0)| 00:00:01 | 1 | 29 | | |
| 8 | VIEW | TEST | 3840K| 974M| 5273K (1)| 00:03:27 | | | | |
| 9 | SORT GROUP BY | | 3840K| 201M| 5273K (1)| 00:03:27 | | | | |
| 10 | JOIN FILTER USE | :BF0000 | 994M| 50G| 5273K (1)| 00:03:27 | | | | |
| 11 | NESTED LOOPS | | 994M| 50G| 5273K (1)| 00:03:27 | | | | |
| 12 | INDEX FULL SCAN | PK_ID | 3840K| 51M| 66212 (1)| 00:00:03 | | | | |
|* 13 | INDEX STORAGE FAST FULL SCAN | IDX_MN_AN_AD_ALL | 259 | 10619 | 1 (0)| 00:00:01 | | | | |
| 14 | REMOTE | | | | | | | | DB2 | R->S |
----------------------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(ROWNUM<10)
2 - access("A_OUT"."ID"="Z_OUT"."ID")
7 - access("A_OUT"."CREATED">TRUNC(SYSDATE@!))
13 - filter("B2"."ATTR_1"=NVL(CASE WHEN (SUBSTR("ID",(-1))<'5' AND EXISTS (SELECT 0 FROM "A1")) THEN 'YYY' END ,'#') OR
"B2"."ATTR_1"='*')
Remote SQL Information (identified by operation id):
----------------------------------------------------
14 - EXPLAIN PLAN INTO PLAN_TABLE@! FOR SELECT 0 FROM "DUAL" "A1" (accessing 'DB2' )
我希望视图被访问n
次数,就像在第一个场景中一样。我尝试使用提示但没有成功。
可能有用的是,即使and exists(select 1 from dual@db2)
视图中未注释该行,以下查询也具有出色的性能(我知道这与 不同Q1
)。
select
(select value from test z_out where a_out.id=z_out.id) as value,
a_out.id
from orders a_out
where a_out.created>trunc(sysdate) and rownum<10
因此,我想n
即使该行and exists(select 1 from dual@db2)
未注释,该视图在被访问时也能正常工作。但我无法强制执行计划朝那个方向发展。
如果需要提示,我只想将它们添加到视图 DDL 中(如果可能的话),这样使用视图的人就不必担心了。
==================================================== ===============
编辑:执行了以下操作:
alter session set statistics_level = 'ALL';
-- Q1 (the query I'm having problems with)
select * from table (dbms_xplan.display_cursor (format=>'ALLSTATS LAST'));
Plan hash value: 3271081243
------------------------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads | OMem | 1Mem | Used-Mem |
------------------------------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 0 |00:00:00.01 | 0 | 0 | | | |
|* 1 | COUNT STOPKEY | | 1 | | 0 |00:00:00.01 | 0 | 0 | | | |
|* 2 | HASH JOIN | | 1 | 1 | 0 |00:00:00.01 | 0 | 0 | 3789K| 3789K| 1078K (0)|
| 3 | JOIN FILTER CREATE | :BF0000 | 1 | 1 | 25602 |00:00:00.22 | 23345 | 161 | | | |
| 4 | PARTITION LIST ALL | | 1 | 1 | 25602 |00:00:00.21 | 23345 | 161 | | | |
| 5 | PARTITION RANGE ALL | | 2 | 1 | 25602 |00:00:00.21 | 23345 | 161 | | | |
| 6 | TABLE ACCESS BY LOCAL INDEX ROWID| ORDERS | 29 | 1 | 25602 |00:00:00.20 | 23345 | 161 | | | |
|* 7 | INDEX RANGE SCAN | IDX_CREATED | 13 | 1 | 25602 |00:00:00.12 | 474 | 161 | 1025K| 1025K| |
| 8 | VIEW | TEST | 1 | 3820K| 0 |00:00:00.01 | 0 | 0 | | | |
| 9 | SORT GROUP BY | | 1 | 3820K| 0 |00:00:00.01 | 0 | 0 | 73728 | 73728 | |
| 10 | JOIN FILTER USE | :BF0000 | 1 | 989M| 106M|00:03:38.87 | 60M| 52960 | | | |
| 11 | NESTED LOOPS | | 1 | 989M| 328M|00:03:04.11 | 60M| 52960 | | | |
| 12 | INDEX FULL SCAN | PK_ID | 1 | 3820K| 1245K|00:00:21.04 | 200K| 52959 | 1025K| 1025K| |
|* 13 | INDEX STORAGE FAST FULL SCAN | IDX_MN_AN_AD_ALL | 1245K| 259 | 328M|00:02:12.09 | 60M| 1 | 1025K| 1025K| |
| 14 | REMOTE | | 1 | | 1 |00:00:00.01 | 0 | 0 | | | |
------------------------------------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(ROWNUM<10)
2 - access("A_OUT"."ID"="Z_OUT"."ID")
7 - access("A_OUT"."CREATED">TRUNC(SYSDATE@!))
13 - filter(("B2"."ATTR_1"=NVL(CASE WHEN (SUBSTR("ID",(-1))<'5' AND IS NOT NULL) THEN 'YYY' END ,'#') OR "B2"."ATTR_1"='*'))
注意:如果视图中未注释,Q1
性能会阻止查询完成。and exists(select 1 from dual@db2)
要获得之前的执行计划,我必须更改会话、运行Q1
、停止Q1
(大约 4 分钟后),然后计算计划。
以下执行计划以相同的方式生成,但视图已--and exists(select 1 from dual@db2)
注释行(性能良好)。
Plan hash value: 2906430222
-----------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-----------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 9 |00:00:00.01 | 223 |
|* 1 | COUNT STOPKEY | | 1 | | 9 |00:00:00.01 | 223 |
| 2 | NESTED LOOPS | | 1 | 1 | 9 |00:00:00.01 | 223 |
| 3 | PARTITION LIST ALL | | 1 | 1 | 9 |00:00:00.01 | 41 |
| 4 | PARTITION RANGE ALL | | 1 | 1 | 9 |00:00:00.01 | 41 |
| 5 | TABLE ACCESS BY LOCAL INDEX ROWID| ORDERS | 14 | 1 | 9 |00:00:00.01 | 41 |
|* 6 | INDEX RANGE SCAN | IDX_CREATED | 12 | 1 | 9 |00:00:00.01 | 33 |
| 7 | VIEW PUSHED PREDICATE | TEST | 9 | 1 | 9 |00:00:00.01 | 182 |
|* 8 | FILTER | | 9 | | 9 |00:00:00.01 | 182 |
| 9 | SORT AGGREGATE | | 9 | 1 | 9 |00:00:00.01 | 182 |
| 10 | NESTED LOOPS | | 9 | 259 | 2376 |00:00:00.01 | 182 |
|* 11 | INDEX UNIQUE SCAN | PK_ID | 9 | 1 | 9 |00:00:00.01 | 20 |
|* 12 | INDEX STORAGE FAST FULL SCAN | IDX_MN_AN_AD_ALL | 9 | 259 | 2376 |00:00:00.01 | 162 |
-----------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(ROWNUM<10)
6 - access("A_OUT"."CREATED">TRUNC(SYSDATE@!))
8 - filter(COUNT(*)>0)
11 - access("ID"="A_OUT"."ID")
12 - storage(("B2"."ATTR_1"=NVL(CASE WHEN SUBSTR("ID",(-1))<'5' THEN 'YYY' END ,'#') OR
"B2"."ATTR_1"='*'))
filter(("B2"."ATTR_1"=NVL(CASE WHEN SUBSTR("ID",(-1))<'5' THEN 'YYY' END ,'#') OR
"B2"."ATTR_1"='*'))