Oracle SQL 性能优化

Posted

技术标签:

【中文标题】Oracle SQL 性能优化【英文标题】:Oracle SQL performance optimization 【发布时间】:2018-08-27 03:58:58 【问题描述】:

我有两条 SQL 语句,我希望它们的性能相似,但实际上 SQL1 使用了 0.065 秒,而 SQL2 使用了超过 10 秒,总共只有 8000 条记录。谁能帮忙解释一下?如何优化 SQL2?

SQL 1:

select
    job_id,
    JOB_DESCRIPTION,
    REGEXP_COUNT(JOB_Description, '(ABC|DEF)([[:digit:]])5') as occurrences 
from smms.job 
where TO_NUMBER(to_char(CREATE_DATE,'YYYY')) = 2017;

SQL 2:

select job_id, JOB_Description 
from (
    select 
        job_id, 
        JOB_DESCRIPTION,
        REGEXP_COUNT(JOB_Description, '(ABC|DEF)([[:digit:]])5') as occurrences 
    from smms.job 
    where TO_NUMBER(to_char(CREATE_DATE,'YYYY')) = 2017
) 
where occurrences > 0;

【问题讨论】:

虽然这与您的问题有些不同,但为什么“SQL 2”使用子查询? occurrences 没有在最终的选择列表中使用,所以我不明白你为什么不在WHERE 子句中直接使用REGEXP_COUNT。即SELECT job_id, job_description FROM sims.job WHERE TO_NUMBER... =2017 AND REGEXP_COUNT... > 0 查询的执行计划是什么?如果将TO_NUMBER(to_char(CREATE_DATE,'YYYY'))=2017 更改为CREATE_DATE >= DATE'2017-01-01' AND CREATE_DATE < DATE'2018-01-01' 会发生什么? 你说的这个子句是我的第一个版本,但是也需要10多秒,所以我尝试了差异子句来优化它。 我猜想版本 1 能够将相当昂贵的正则表达式操作限制为较小的结果集 - 两个版本的执行计划应该显示差异(或者可能是带有事件 10046 的 sql 跟踪)。 create_date 有索引吗? SQL3: select count(*) from smms.job where REGEXP_COUNT(JOB_Description, '(ABC|DEF)([[:digit:]])5')>0, 需要 10秒,这意味着性能与 CREATE_DATE 过滤器无关,它完全取决于 REGEXP_COUNT 子句。 【参考方案1】:

再次思考信息,我猜这两种策略是:

SQL 1:

TO_NUMBER(to_char(CREATE_DATE,'YYYY')) = 2017过滤行 在结果行上使用函数REGEXP_COUNT(JOB_Description, '(ABC|DEF)([[:digit:]])5')

SQL 2:

在所有行上使用函数REGEXP_COUNT(JOB_Description, '(ABC|DEF)([[:digit:]])5')TO_NUMBER(to_char(CREATE_DATE,'YYYY')) = 2017过滤结果

由于正则表达式函数在 Oracle 中非常昂贵,这可以解释性能差异。

版本 2 可以通过提示进行优化 - 例如使用 MATERIALIZE,如果您添加 CTE。

【讨论】:

是的,你是对的!如果我在 TO_NUMBER(to_char(CREATE_DATE,'YYYY')) = 2017 上使用 /*+ materialize */ 首先使用 CTE,然后应用 REGEXP_COUNT,它极大地提高了性能,没有具体化提示,无论我如何重新组织过滤子句,执行计划和处理时间是一样的。非常感谢!【参考方案2】:

正如 Martin 所指出的,问题在于昂贵的 regexp_count 函数。所以减少问题是:

为什么:

  select * from (
  with dat as (select level lv, rpad('X',500,'X') txt from dual connect by level <= 20000)
  select lv, 
         REGEXP_COUNT(txt, '(ABC|DEF)([[:digit:]])5') as occurrences 
  from   dat 
  --where  REGEXP_COUNT(txt, '(ABC|DEF)([[:digit:]])5') > 1
  ) where rownum > 1

0.019 秒和

  select * from (
  with dat as (select level lv, rpad('X',500,'X') txt from dual connect by level <= 20000)
  select lv, 
         REGEXP_COUNT(txt, '(ABC|DEF)([[:digit:]])5') as occurrences 
  from   dat 
  where  REGEXP_COUNT(txt, '(ABC|DEF)([[:digit:]])5') > 1
  ) where rownum > 1

6.7 秒。 Oracle 在两次执行中评估 regexp_count。所以在 where 部分和 select 部分的评价肯定是有区别的。

【讨论】:

我不太明白你的意思,但是rownum > 1可以实现SQL,从而提高性能。 rownum > 1 不是为了提高性能。它是检查语句需要的时间,而不需要走到游标的末尾。因此,在查询 1 和 2 中执行相同的操作,在 2 中它的因素较慢。【参考方案3】:

在 SQL1 中,它按 (TO_NUMBER(to_char(CREATE_DATE,'YYYY')) = 2017 过滤 对于返回的行,每行执行 (REGEXP_COUNT)

在 SQL2 中,它通过 (REGEXP_COUNT) 的结果进行过滤,这意味着对所有表行执行它。然后,根据该结果过滤 (TO_NUMBER(to_char(CREATE_DATE,'YYYY')) = 2017)

为了证明这一点,请在没有过滤器的情况下执行 SQL1。这将花费大约与 SQL2 一样多的时间,也许更多。

要进行优化,您需要 100% 确定它会首先使用 SQL1 过滤器。一种绝对的方法是执行 SQL1 并将结果放入临时/内存表中,然后对它们进行过滤 SQL2 过滤器

【讨论】:

以上是关于Oracle SQL 性能优化的主要内容,如果未能解决你的问题,请参考以下文章

Oracle SQL 性能优化利器

Oracle SQL 性能优化

oracle性能优化

Oracle SQL语句性能优化方法大全

Oracle SQL性能优化

Oracle数据库性能优化