通过 FETCH NEXT 子句加速 WHERE EXISTS 子句
Posted
技术标签:
【中文标题】通过 FETCH NEXT 子句加速 WHERE EXISTS 子句【英文标题】:Speeding up WHERE EXISTS clause by FETCH NEXT clause 【发布时间】:2018-07-24 17:01:04 【问题描述】:我有一个长期运行的 Oracle 查询,它使用了一堆:
WHERE EXISTS (SELECT NULL FROM Table WHERE TableColumn IN (...))
而不是使用SELECT NULL
,它遍历整个表来查找条件,我不能只在它后面加上FETCH NEXT 1 ROW ONLY
,因为我只关心TableColumn
是否是IN (...)
?
像这样:
WHERE EXISTS (SELECT NULL FROM Table WHERE TableColumn IN (...) FETCH NEXT 1 ROW ONLY)
因此WHERE EXISTS
的评估速度会更快。
编辑:
以下是未附加FETCH NEXT
子句的查询计划:
------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Time |
------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 75 | 521611 | |
| 1 | SORT AGGREGATE | | 1 | 75 | | |
| 2 | HASH JOIN | | 531266 | 39844950 | 521611 | |
| 3 | TABLE ACCESS FULL | ACCT | 47574 | 523314 | 418 | |
| 4 | HASH JOIN | | 531224 | 33998336 | 521185 | |
| 5 | INDEX FAST FULL SCAN | PK_ACTVTYP | 454 | 2270 | 2 | |
| 6 | HASH JOIN | | 531224 | 31342216 | 521177 | |
| 7 | INDEX FULL SCAN | PK_ACTVCAT | 67 | 335 | 1 | |
| 8 | HASH JOIN SEMI | | 531224 | 28686096 | 521169 | |
| 9 | NESTED LOOPS SEMI | | 531224 | 28686096 | 521169 | |
| 10 | STATISTICS COLLECTOR | | | | | |
| 11 | HASH JOIN RIGHT SEMI | | 531224 | 25498752 | 112887 | |
| 12 | TABLE ACCESS FULL | AMSACTVGRPEMPL | 2364 | 35460 | 10 | |
| 13 | TABLE ACCESS FULL | ACTV | 12779986 | 421739538 | 112712 | |
| 14 | INDEX RANGE SCAN | ACTVSUBACTV_DX2 | 163091724 | 978550344 | 251246 | |
| 15 | INDEX FAST FULL SCAN | ACTVSUBACTV_DX2 | 163091724 | 978550344 | 251246 | |
------------------------------------------------------------------------------------------------
下面是带有FETCH NEXT
子句的查询计划:
------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Time |
------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 69 | 113148 | |
| 1 | SORT AGGREGATE | | 1 | 69 | | |
| 2 | FILTER | | | | | |
| 3 | HASH JOIN | | 531221 | 36654249 | 113144 | |
| 4 | TABLE ACCESS FULL | ACCT | 47574 | 523314 | 418 | |
| 5 | HASH JOIN | | 531179 | 30808382 | 112718 | |
| 6 | INDEX FAST FULL SCAN | PK_ACTVTYP | 454 | 2270 | 2 | |
| 7 | HASH JOIN | | 531179 | 28152487 | 112710 | |
| 8 | INDEX FULL SCAN | PK_ACTVCAT | 67 | 335 | 1 | |
| 9 | HASH JOIN RIGHT SEMI | | 531179 | 25496592 | 112702 | |
| 10 | TABLE ACCESS FULL | AMSACTVGRPEMPL | 2167 | 32505 | 10 | |
| 11 | TABLE ACCESS FULL | ACTV | 12778893 | 421703469 | 112527 | |
| 12 | VIEW | | 1 | 13 | 4 | |
| 13 | WINDOW BUFFER PUSHED RANK | | 8 | 48 | 4 | |
| 14 | INDEX RANGE SCAN | ACTVSUBACTV_DX2 | 8 | 48 | 4 | |
------------------------------------------------------------------------------------------------
据我所知,如果没有 FETCH NEXT
,它会增加更多 TABLE ACCESS FULL
的开销
编辑#2
添加AND ROWNUM = 1
而不是FETCH NEXT 1 ROW ONLY
:
------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Time |
------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 54 | 128114 | |
| 1 | SORT AGGREGATE | | 1 | 54 | | |
| 2 | FILTER | | | | | |
| 3 | HASH JOIN | | 12779902 | 690114708 | 113296 | |
| 4 | TABLE ACCESS FULL | ACCT | 47574 | 523314 | 418 | |
| 5 | HASH JOIN | | 12778893 | 549492399 | 112713 | |
| 6 | MERGE JOIN CARTESIAN | | 30418 | 304180 | 31 | |
| 7 | INDEX FULL SCAN | PK_ACTVCAT | 67 | 335 | 1 | |
| 8 | BUFFER SORT | | 454 | 2270 | 30 | |
| 9 | INDEX FAST FULL SCAN | PK_ACTVTYP | 454 | 2270 | 0 | |
| 10 | TABLE ACCESS FULL | ACTV | 12778893 | 421703469 | 112517 | |
| 11 | COUNT STOPKEY | | | | | |
| 12 | INLIST ITERATOR | | | | | |
| 13 | INDEX UNIQUE SCAN | PK_AMSACTVGRPEMPL | 1 | 15 | 2 | |
| 14 | COUNT STOPKEY | | | | | |
| 15 | INDEX RANGE SCAN | ACTVSUBACTV_DX2 | 2 | 12 | 4 | |
------------------------------------------------------------------------------------------------
【问题讨论】:
您是否使用 EXPLAIN PLAN 分析了查询以查看瓶颈在哪里? @MickMnemonic 我有,但我也想加快其他部分的速度。我使用 Dummy 数据进行了测试,并将FETCH NEXT 1 ROW ONLY
添加到几个 WHERE EXISTS
子句中已经为我节省了 100 秒。
exists()
不会遍历整个表,它会检查是否存在任何行 - 这就是为什么它比计数并看到计数大于零要好。 in()
的使用更可能是一个问题,但您需要查看执行计划以了解它到底在做什么。
@AlexPoole 为什么添加FETCH NEXT 1 ROW ONLY
会加快查询速度呢?
奇怪。一旦找到记录,EXISTS
应该始终停止查找。 FETCH
子句和 ROWNUM
条件都不应该有任何效果。子查询甚至不相关。你当然应该在TableColumn
上有一个索引——至少如果Table
是一张大桌子。
【参考方案1】:
FETCH NEXT
在 12c 中是新的,为了避免导致它添加的性能问题
提示如下
WHERE EXISTS (SELECT /*+ first_rows(1)*/* FROM Table WHERE TableColumn IN (...) FETCH NEXT 1 ROW ONLY)
试一试并检查它的查询计划
注意:我建议在 ACCT ,ACTV 表上添加索引以提高其性能。
【讨论】:
以上是关于通过 FETCH NEXT 子句加速 WHERE EXISTS 子句的主要内容,如果未能解决你的问题,请参考以下文章
进行查询后,如何通过 Collection 中的 where 子句获取选择的模型
通过 FMDB 将数组传递给 sqlite WHERE IN 子句?