我有一个oracle SQL语句的奇怪性能问题。该语句是一个或多或少的giantic subselect / inner join语句,因此我只能在这里发布它的结构。它看起来像这样:
SELECT "A".COL1, [...] FROM "A"
INNER JOIN ( .. massive amount of subselects and joins ... )
WHERE [...]
该声明的速度非常快(约30秒)。为了进一步提高速度,我决定按时间限制选择:
SELECT "A".COL1, [...] FROM "A"
INNER JOIN ( .. massive amount of subselects and joins ... )
WHERE "A".TIMESTAMP > ... AND [...]
这产生了完全相反的效果。声明执行时间现在超过600秒(!!)。
解释计划现在设置完全不同(正如我所说,仅仅因为一个单一的MORE限制 - 限制具有完整的索引)。之前是加入,索引限制和快速全扫描的“正常”组合。之后,它完全搞砸了成千上万的NESTED LOOPS。
我知道这很难从外面说出来,但是有什么可以导致这些嵌套循环的一般提示吗? EXPLAIN计划开始(!!)之前:“正常”哈希的组合加入限制等等。深度始终< 10
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 461 | 286K| 1672 (5)| 00:00:11 |
| 1 | SORT GROUP BY | | 461 | 286K| 1672 (5)| 00:00:11 |
|* 2 | HASH JOIN | | 461 | 286K| 1671 (5)| 00:00:11 |
| 3 | VIEW | index$_join$_016 | 2822 | 93126 | 21 (5)| 00:00:01 |
|* 4 | HASH JOIN | | | | | |
|* 5 | INDEX RANGE SCAN | HRP1000~0 | 2822 | 93126 | 5 (0)| 00:00:01 |
|* 6 | INDEX FAST FULL SCAN | HRP1000~1 | 2822 | 93126 | 19 (0)| 00:00:01 |
|* 7 | HASH JOIN | | 459 | 270K| 1649 (5)| 00:00:11 |
|* 8 | HASH JOIN | | 459 | 259K| 1609 (5)| 00:00:10 |
|* 9 | TABLE ACCESS FULL | BBP_PDORG | 14463 | 607K| 39 (0)| 00:00:01 |
|* 10 | HASH JOIN | | 1939 | 1013K| 1569 (5)| 00:00:10 |
|* 11 | HASH JOIN RIGHT OUTER | | 691 | 335K| 1548 (5)| 00:00:10 |
| 12 | VIEW | | 1572 | 47160 | 148 (5)| 00:00:01 |
| 13 | HASH GROUP BY | | 1572 | 411K| 147 (5)| 00:00:01 |
之后 - 大量的嵌套循环。深度> 20
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time
----------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 1392 | 329 (6)| 00:00:03
| 1 | SORT GROUP BY | | 1 | 1392 | 328 (5)| 00:00:03
| 2 | NESTED LOOPS | | 1 | 1392 | 327 (5)| 00:00:03
| 3 | NESTED LOOPS | | 1 | 1371 | 327 (5)| 00:00:03
| 4 | NESTED LOOPS | | 1 | 1333 | 327 (5)| 00:00:03
| 5 | NESTED LOOPS | | 1 | 1312 | 327 (5)| 00:00:03
| 6 | NESTED LOOPS | | 1 | 1274 | 326 (5)| 00:00:03
| 7 | NESTED LOOPS | | 1 | 1235 | 326 (5)| 00:00:03
| 8 | NESTED LOOPS | | 1 | 1196 | 326 (5)| 00:00:03
| 9 | NESTED LOOPS | | 1 | 1175 | 326 (5)| 00:00:03
| 10 | NESTED LOOPS | | 1 | 1137 | 325 (5)| 00:00:03
| 11 | NESTED LOOPS | | 1 | 1116 | 325 (5)| 00:00:03
| 12 | NESTED LOOPS | | 1 | 1078 | 325 (5)| 00:00:03
| 13 | NESTED LOOPS | | 1 | 1061 | 325 (5)| 00:00:03
| 14 | NESTED LOOPS | | 1 | 1010 | 324 (5)| 00:00:03
| 15 | NESTED LOOPS | | 1 | 988 | 324 (5)| 00:00:03
|* 16 | HASH JOIN | | 1 | 953 | 324 (5)| 00:00:03
| 17 | NESTED LOOPS | | | | |
| 18 | NESTED LOOPS | | 1 | 898 | 284 (6)| 00:00:02
| 19 | NESTED LOOPS | | 1 | 853 | 284 (6)| 00:00:02
|* 20 | HASH JOIN | | 1 | 823 | 284 (6)| 00:00:02
| 21 | NESTED LOOPS | | 1 | 780 | 236 (6)| 00:00:02
| 22 | NESTED LOOPS | | 1 | 741 | 236 (6)| 00:00:02
| 23 | NESTED LOOPS | | 1 | 701 | 235 (6)| 00:00:02
| 24 | NESTED LOOPS | | 1 | 639 | 235 (6)| 00:00:02
| 25 | NESTED LOOPS | | 1 | 609 | 235 (6)| 00:00:02
| 26 | NESTED LOOPS | | 1 | 576 | 235 (6)| 00:00:02
| 27 | NESTED LOOPS | | 1 | 533 | 234 (6)| 00:00:02
| 28 | NESTED LOOPS | | 1 | 495 | 234 (6)| 00:00:02
答案 0 :(得分:0)
以下可能是时间增加的原因: -
索引: - 如果假设您的上一个查询的条件是(其中col1 =''和col2 ='')并且您已经在col1,col2上创建了一个复合索引,那么您的查询将使用索引并执行索引快速全扫描等。如果您的时间戳过滤器未编入索引,则您的查询不再使用索引,而是进行全表扫描(增加时间)
TIMESTAMP COLUMN PLACEMENT: - 由于错误放置时间戳,您的解释计划的深度会增加。我们知道我们的查询从结束开始运行,即假设你的查询在条件就好的地方(其中col1 =''和col2 =''和col3 =''),所以你的数据将首先根据col3过滤然后col2然后再过滤col1。因此,如果在子查询中添加了此时间戳条件。每次您的查询将根据时间戳进行过滤,因为我们知道每次处理外部查询时子查询都会运行。
所以我建议如果你在子查询中使用时间戳,那么修改它。 建议: - 分析查询或派生查询比子查询运行得更快,因为子查询的限制是外部查询的每一行都处理内部查询
答案 1 :(得分:0)
优化器可能认为A.TIMESTAMP > ...
会减少命中数,以至于对于少量行使用嵌套循环比执行大型连接更便宜。
根据所提供的稀缺信息,确切的原因以及是否有一种简单的方法来纠正问题很难确定。
当您添加索引(或索引列上的条件)时,执行计划会发生巨大变化,您不应感到惊讶。
我有点惊讶它选择改变>
比较的计划。限制是固定值(即优化器是否已知)并且是否接近表中的最高值(如表统计中所记录的那样)?
关于时间戳有一个警告,那就是最高值的统计数据可以很快过时。 假设您的统计数据是24小时,并且您正在寻找过去24小时内的日期。优化器将使用统计信息并预测查询将导致0次点击。所以它将从检查索引开始。
实际上,您在过去24小时内输入了大量新记录。一整天的新记录...
将优化器设置为直接的一种方法是将截止日期作为参数提供(并在适用的情况下预编译问题),以便优化器不会误以为它会获得0次点击。