Оптимизация производительности Oracle SQL - PullRequest
0 голосов
/ 27 августа 2018

У меня есть два оператора 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;

Ответы [ 3 ]

0 голосов
/ 27 августа 2018

Как указал Мартин, проблема в дорогой функции 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.

0 голосов
/ 18 октября 2018

В 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

0 голосов
/ 27 августа 2018

снова обдумывая информацию, я думаю, что две стратегии:

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.

...