Оптимизация запросов на Oracle - PullRequest
0 голосов
/ 21 февраля 2019

Я не эксперт по производительности запросов, и я изучаю, как оптимизатор Oracle работает с различными запросами, и настраиваю их для его использования.Ниже приведен такой запрос из моего проекта, в котором я застрял на оптимизации его для большого набора данных (он замедляется для большого набора данных).

SELECT
     v1.id,
     v1.date_created,
     v1.name,
     v1.size
 FROM
      ver v1
     INNER JOIN (
         SELECT
             id,
             MAX(date_created) AS last_date_created
         FROM
             ver
         WHERE
             id IN (
                 ...500 ids
             )
             AND active = 'Y'
             AND archived = 'N'
         GROUP BY
             id
     ) v2 ON v1.date_created = v2.last_date_created
             AND v1.id = v2.id

Я пробовал советник по настройке запросов для разработчиков SQL, без рекомендаций.Проблема здесь в том, что он собирается для полного сканирования таблицы в обеих частях запроса и не использует индекс, а таблица ver содержит почти 1 млн записей.Ниже приведен скрипт таблицы ver

create table ver 
 (  "Ver_id" VARCHAR2(36 BYTE) Primary key
    "NAME" VARCHAR2(255 BYTE) 
    "ACTIVE" VARCHAR2(1 BYTE) 
    "ARCHIVED" VARCHAR2(1 BYTE) 
    "DESCRIPTION" VARCHAR2(255 BYTE), 
    "ID" VARCHAR2(36 BYTE) 
    "DATE_CREATED" NUMBER(*,0)
    "CREATED_BY_USER" VARCHAR2(64 BYTE) 
    "SIZE" NUMBER(*,0)
    "LAST_MODIFIED" NUMBER(*,0))

, а индексы - это один неуникальный индекс по идентификатору и один уникальный индекс по (id, name) и неуникальный для last_modified.

Запрос занимает почти 2-3мин за его исполнение сейчас.Любые предложения по этому поводу.

Ответы [ 2 ]

0 голосов
/ 21 февраля 2019

Если вы выбираете только 2–3 K строк в таблице строк 1M, вы можете извлечь выгоду из использования индекса.

В основном вы хотите: а) выбрать все строки с определенным списком ID s и b) фильтр для каждой ID только записи (записей) с MAX date_created.

Вам нужен только индекс для ID

create index ver_idx on ver(id);

Вот две альтернативные стратегии, которые могутиспользоваться:

Использовать аналитические функции для получения последней строки

В подзапросе вы получаете все строки с указанными ID s и аналитической функцией RANKВы определяете порядок строк.Основной запрос выбирает только строки с rn = 1, т. Е. С max (date_created).

Обратите внимание, что я использую RANK, чтобы получить тот же результат, что и ваш запрос.Если на максимальном уровне есть связи, вы получите больше записей.Вы можете использовать ROW_NUMBER, если вам нужна только одна запись даже для связей.

with dt as (
select 
 id,date_created, name, "SIZE",
rank() over (partition by id order by date_created desc) rn
from ver
where id between 1 and 500
AND active = 'Y'
AND archived = 'N')
select 
  id,date_created, name, "SIZE"
from dt
where rn = 1;

Вы используете индекс, чтобы получить все строки с выбранными ID s с дополнительными фильтрами, чтобы получить только строки с максимальной датой.

Использовать коррелированный подзапрос

Вы используете коррелированный подзапрос для фильтрации строк с максимальной датой:

select 
 id,date_created, name, "SIZE"
from ver a
where id between 1 and 500  
AND active = 'Y'
AND archived = 'N'
AND date_created in (select max(date_created) 
                     from ver where id = a.id and active = a.active 
                              and archived = a.archived)

Невозможно сказать, какиеподход самый лучший.Это зависит от данных в вашей таблице.

Простой тест, проверьте планы выполнения и найдите наиболее эффективный запрос.

0 голосов
/ 21 февраля 2019

Прежде всего вам не нужно иметь индекс для id, так как у вас уже есть индекс для пары (id,name).

вы можете иметь индекс Btree для версии (id, date_created) и индекс растрового изображения для ver(active,Archived)

вы также не используете v2 в операторе SELECT, таким образом, вышеупомянутый запрос может быть переписанкак

SELECT
     v1.id,
     v1.date_created,
     v1.name,
     v1.size
 FROM
      ver v1
 WHERE EXISTS (
     SELECT 1
     FROM ver v2
     WHERE v1.id = v2.id 
       and v2.id IN (
            ...500 ids
       )
       AND v2.active = 'Y'
       AND v2.archived = 'N'
     GROUP BY v2.id
     HAVING MAX(v2.date_created) = v1.date_created
 )
...