【问题标题】:Query much slower with a where clause on column which has index在具有索引的列上使用 where 子句查询要慢得多
【发布时间】:2022-01-24 01:17:04
【问题描述】:

下面的查询表现不错

    select distinct i.TERMINALNAME, 
           to_char(i.BEGINTIME,'mm/dd/yyyy hh:mi:ss AM') BEGINTIME,
           i.ERRORTEXT, 
           i.RECORDSPROCESSED, 
           i.RECORDTYPE 
      from MYLOGGING i
inner join (select recordtype, 
                   max(BEGINTIME) as lastrundate 
              from MYLOGGING group by recordtype) im on 
      im.recordtype=i.recordtype and im.lastrundate=i.BEGINTIME
     where i.ERRORTEXT in ('Success', 'Failure') 
       and i.TERMINALNAME Not In ('REE300', 'XEE300', 'YT', 'QX', 'VC', 'DF') 
    ORDER BY i.TERMINALNAME ASC;

QUERY PLAN

Plan hash value: 3900617130
 
------------------------------------------------------------------------------------------
| Id  | Operation             | Name             | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |                  |     2 |   132 |  1257   (2)| 00:00:01 |
|   1 |  SORT UNIQUE          |                  |     2 |   132 |  1256   (2)| 00:00:01 |
|*  2 |   HASH JOIN RIGHT SEMI|                  |     2 |   132 |  1255   (2)| 00:00:01 |
|   3 |    VIEW               |                  |   772 | 16984 |   630   (2)| 00:00:01 |
|   4 |     HASH GROUP BY     |                  |   772 | 16212 |   630   (2)| 00:00:01 |
|   5 |      TABLE ACCESS FULL| MYLOGGING |   281K|  5765K|   623   (1)| 00:00:01 |
|*  6 |    TABLE ACCESS FULL  | MYLOGGING |   191K|  8222K|   625   (1)| 00:00:01 |
------------------------------------------------------------------------------------------

但是,在查询 and i.BEGINTIME>= trunc(sysdate) - 60 的末尾添加了新的 where 子句,如果第一次运行,查询会运行得更慢,第二次运行后会很快。

     select distinct i.TERMINALNAME, 
               to_char(i.BEGINTIME,'mm/dd/yyyy hh:mi:ss AM') BEGINTIME, 
               i.ERRORTEXT, 
               i.RECORDSPROCESSED, 
               i.RECORDTYPE 
          from MYLOGGING i
    inner join (select recordtype, 
                       max(BEGINTIME) as lastrundate 
                  from MYLOGGING group by recordtype) im on im.recordtype=i.recordtype 
           and im.lastrundate=i.BEGINTIME
         where i.ERRORTEXT in ('Success', 'Failure') 
           and i.TERMINALNAME Not In ('REE300', 'XEE300', 'YT', 'QX', 'VC', 'DF') 
           and i.BEGINTIME>= trunc(sysdate) - 60 ORDER BY i.TERMINALNAME ASC;

查询计划

Plan hash value: 2346866897
 
----------------------------------------------------------------------------------------------------------------
| Id  | Operation                               | Name                 | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                        |                      |     1 |    86 |   809   (1)| 00:00:01 |
|   1 |  SORT UNIQUE                            |                      |     1 |    86 |   809   (1)| 00:00:01 |
|*  2 |   FILTER                                |                      |       |       |            |          |
|   3 |    SORT GROUP BY                        |                      |     1 |    86 |   809   (1)| 00:00:01 |
|*  4 |     HASH JOIN                           |                      | 32572 |  2735K|   807   (1)| 00:00:01 |
|*  5 |      TABLE ACCESS BY INDEX ROWID BATCHED| MYLOGGING     |     1 |    64 |     4   (0)| 00:00:01 |
|*  6 |       INDEX RANGE SCAN                  | IX2_MYLOGGING |     1 |       |     3   (0)| 00:00:01 |
|   7 |      TABLE ACCESS FULL                  | MYLOGGING     |   283K|  6081K|   802   (1)| 00:00:01 |
----------------------------------------------------------------------------------------------------------------

查询计划显示查询运行速度很快,因为这不是我第一次运行它,我假设数据已缓存。我没有权限刷新缓存。

知道是什么导致查询在第一次运行时运行速度非常慢吗?超过2-3分钟。目前,BEGINTIME 列上的索引将“join_index”设置为 no。会不会有什么关系?

【问题讨论】:

    标签: oracle


    【解决方案1】:

    我会首先缩小 MYLOGGING 表的范围,这样它就可以减少搜索并将其放入另一个子查询中。按日期缩小范围。因为 FROM 发生在 WHERE 之前。

    select distinct i.TERMINALNAME
    , to_char(i.BEGINTIME,'mm/dd/yyyy hh:mi:ss AM') BEGINTIME
    , i.ERRORTEXT
    , i.RECORDSPROCESSED
    , i.RECORDTYPE 
    from 
    (select i2.*
     from MYLOGGING i2
     where i2.BEGINTIME >= trunc(sysdate) - 60
    ) i
    inner join (select recordtype
                , max(BEGINTIME) as lastrundate 
                from MYLOGGING 
                group by recordtype
               ) im on im.recordtype=i.recordtype 
                and im.lastrundate=i.BEGINTIME
         where i.ERRORTEXT in ('Success', 'Failure') and i.TERMINALNAME Not In ('REE300', 'XEE300', 'YT', 'QX', 'VC', 'DF')  ORDER BY i.TERMINALNAME ASC;
    

    【讨论】:

      【解决方案2】:

      您可以通过将自联接转换为分析函数来避免性能问题。没有连接,优化器可以做的工作更少,选择错误计划的方法也更少。

      select distinct
          TERMINALNAME, 
          to_char(BEGINTIME,'mm/dd/yyyy hh:mi:ss AM') BEGINTIME, 
          ERRORTEXT, 
          RECORDSPROCESSED, 
          RECORDTYPE
      from
      (
          select MYLOGGING.*, max(BEGINTIME) over (partition by recordtype) MAX_BEGINTIME_PER_RECORDTYPE
          from MYLOGGING
      )
      where BEGINTIME = MAX_BEGINTIME_PER_RECORDTYPE
        and ERRORTEXT in ('Success', 'Failure') 
        and TERMINALNAME Not In ('REE300', 'XEE300', 'YT', 'QX', 'VC', 'DF') 
        and BEGINTIME>= trunc(sysdate) - 60
      ORDER BY TERMINALNAME ASC;
      

      找出为什么原始查询运行缓慢可能更加困难。您将希望找到带有 actual 数字的 actual 执行计划,而不是仅使用解释计划猜测。有关如何使用/*+ GATHER_PLAN_STATISTICS */DBMS_XPLAN 显示执行计划的信息,请参阅this question

      完整的执行计划,尤其是“注释”部分中的数据,可能会为您提供有关该计划为何缓慢以及为何变得更快的线索。也许有一个动态的重新优化,甲骨文认识到第一次运行很糟糕,并调整了计划。您可能还想尝试使用DBMS_XPLAN.DISPLAY_AWR 查看执行计划的历史记录。

      实际时间以及实际行数与估计行数相比,通常可以告诉您 Oracle 如何以及为何做出错误决定。例如,额外的 WHERE 子句可能导致 Oracle 大大低估了返回的行数,这使得索引访问和嵌套循环操作看起来更快。但是由于几个原因,嵌套循环和索引扫描通常最适合返回一小部分行,而散列连接和全表扫描通常最适合返回大部分行。

      收集和解释这些数据可能需要数小时,尤其是在您不熟悉该过程的情况下。您可以借此机会了解有关查询调优的更多信息,或者如果您没有时间,只需使用上述分析函数方法即可完全避免该问题。

      【讨论】:

        猜你喜欢
        • 2017-02-01
        • 2016-06-26
        • 2012-03-16
        • 1970-01-01
        • 2011-06-13
        • 1970-01-01
        • 1970-01-01
        • 2019-10-03
        相关资源
        最近更新 更多