2013-07-12 5 views
1

У меня есть запрос на получение информации о клиентах, и я добавляю функцию max(), чтобы найти самую последнюю дату заказа. Без агрегата запрос занимает 0,23 секунды для запуска, но с ним требуется 12,75 секунды.Оптимизация агрегированной функции в Oracle

Вот запрос:

SELECT U.SEQ, MAX(O.ORDER_DATE) FROM CUST_MST U 
INNER JOIN ORD_MST O ON U.SEQ = O.CUST_NUM 
WHERE U.SEQ = :customerNumber 
GROUP BY U.SEQ; 

ORD_MST представляет собой таблицу с 890000 записей.

Есть ли более эффективный способ получить эту функциональность?

EDIT: Для записи ничего особенного не мешает мне запускать два запроса и присоединяться к ним в моей программе. Мне кажется невероятно странным, что такой простой запрос займет много времени. В этом случае гораздо проще/проще позволить базе данных объединять информацию, но это не единственный способ для меня сделать это.

EDIT 2: В соответствии с запросом, вот планы по запросам, которые я ссылаюсь в этом вопросе.

With Aggregate

Without Aggregate

+1

Если вы запускаете его дважды подряд, это займет столько же времени во второй раз? –

+0

@ DanBracuk Это довольно непротиворечиво. В лучшем случае я вижу разницу в 2 секунды. – Logarr

+0

Можете ли вы добавить планы запросов для обоих вопросов на вопрос? Кроме того, какие индексы в настоящее время включены в обе таблицы? –

ответ

2

проблема с вашим запросом, что вы присоединитесь обе таблицы полностью, то максимальная функция выполняется против всего результата, и, наконец, где оператор фильтрует ваши строки.

вы должны улучшить соединение, путем просто присоединения строки с определенным CustID вместо полных таблиц, должен выглядеть следующим образом:

SELECT U.SEQ, MAX(O.ORDER_DATE) FROM 
    (SELECT * FROM CUST_MST WHERE SEQ = :customerNumber) U 
INNER JOIN 
    (SELECT * FROM ORD_MST WHERE CUST_NUM = :customerNumber) O ON U.SEQ = O.CUST_NUM 
GROUP BY U.SEQ; 

Другой вариант заключается в использовании заказ и фильтрации первого ROWNUM , это не чистый путь. Может быть быстрее, если нет, вам также потребуется подзапрос, чтобы не заказывать полные таблицы. Didnt использование Оракул на некоторое время, но это должно выглядеть примерно так:

SELECT * FROM 
(
SELECT U.SEQ, O.ORDER_DATE FROM CUST_MST U 
INNER JOIN ORD_MST O ON U.SEQ = O.CUST_NUM 
WHERE U.SEQ = :customerNumber 
GROUP BY U.SEQ; 
ORDER BY O.ORDER_DATE DESC 
) 
WHERE ROWNUM = 1 

Вы вынуждены использовать присоединиться к какой-то причине, почему бы вам не выбрать непосредственно из ORD_MST без присоединиться?

EDIT Еще одна идея:

SELECT * FROM 
(SELECT CUST_NUM, MAX(ORDER_DATE) FROM ORD_MST WHERE CUST_NUM = :customerNumber GROUP BY CUST_NUM) O 
INNER JOIN CUST_MST U ON O.CUST_NUM = U.SEQ 

если внутренний выбор только занимает одну секунду, а затем присоединиться должен работать мгновенно.

+0

Странно, что они оба до тех пор, как мои. Я просто проверил индексы для 'ORD_MST', и я не вижу индекса для' ORDER_DATE'. Я бы подумал, что это может быть проблемой, но тогда я бегу против только ORD_MST. Я получаю результат менее чем за одну секунду. – Logarr

+0

Можете ли вы опубликовать это заявление здесь? у меня может быть другая идея. – Koryu

+0

Запрос, который я использовал в качестве теста, был «SELECT MAX (ORDER_DATE) FROM ORD_MST WHERE CUST_NUM =: customerNumber;'. Ваш последний запрос работал за 1,1 секунды! – Logarr

2

Выполнить эти команды:

Explain plan for 
SELECT U.SEQ, MAX(O.ORDER_DATE) FROM CUST_MST U 
INNER JOIN ORD_MST O ON U.SEQ = O.CUST_NUM 
WHERE U.SEQ = :customerNumber 
GROUP BY U.SEQ; 

select * from table(dbms_xplan.display); 

и разместить результаты здесь. Не зная план исполнения, мы можем только догадываться, что на самом деле происходит.

КПП. я чувствую, что добавление составного индекса для таблицы ORD_MST с столбцами cust_num + order_date может решить проблему (предполагая, что SEQ является первичным ключом для таблицы CUST_MST, и он уже имеет уникальный индекс).Попробуйте:

CREATE INDEX idx_name ON ORD_MST(cust_num, order_date); 

Кроме того, после создания статистики индекса обновления экрана с командами:

EXEC DBMS_STATS.gather_table_stats('your-schema-name', 'CUST_MST'); 
EXEC DBMS_STATS.gather_table_stats('your-schema-name', 'ORD_MST'); 

попробовать ваш запрос.