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 性能优化的主要内容,如果未能解决你的问题,请参考以下文章