通过 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 子句?

Oracle Fetch(限制行数)子句

Oracle Fetch(限制行数)子句

如何通过文本框过滤数据表视图中的子表单? #likeoperator #where 子句

通过在 where 子句中传递值来选择数据,如果值匹配则返回该数据,否则返回所有数据