2012-02-03 4 views
4

Недавно мне было поручено оптимизировать некоторые существующие хранимые процедуры Oracle. Каждая из хранимых процедур запрашивает базу данных и генерирует вывод XML-файла. Один, в частности, занимал около 20 минут, чтобы закончить исполнение. Взглянув на это, было несколько вложенных циклов и ненужных запросов. Например, вместо того, чтобы делать вОптимизация хранимых процедур Oracle

SELECT * from Employee e, Department d WHERE e.DEPT_ID = d.ID 
--write data from query to XML 

это было больше похож

FOR emp_rec in (SELECT * from employee) 
LOOP 
    SELECT * from Department WHERE id = emp_rec.DEPT_ID; 
    --write data from query to XML 
END LOOP; 

Изменения всех этих случаев, чтобы выглядеть как первый вариант ускорил процедуру очень. Почему мой вопрос? Почему выполняется объединение в запросе выбора быстрее, чем вручную комбинирование таблиц? Каковы основные процессы?

ответ

5

Давайте посмотрим, как будет обрабатываться исходная версия.

FOR emp_rec in (SELECT * from employee) 
LOOP 
    SELECT * from Department WHERE id = emp_rec.DEPT_ID; 
    --write data from query to XML 
END LOOP; 

Запрос цикла, скорее всего, сделать полное сканирование таблицы на employee. Затем для каждой возвращаемой строки он выполнит внутренний запрос. Предполагая, что id является первичным ключом department, каждое выполнение запроса, вероятно, будет выполнять уникальный поиск с использованием индекса первичного ключа.

Звучит здорово, правда? Уникальный поиск индексов обычно является самым быстрым способом получения одной строки (за исключением явного поиска по ROWID). Но подумайте о том, что это делается над несколькими итерациями цикла. Предположительно, каждый сотрудник принадлежит отделу; у каждого отдела есть сотрудники; и большинство или все отделы имеют несколько сотрудников.

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

Кроме того, в конечном счете, вы, вероятно, захотите, чтобы каждый ряд находился в department хотя бы один раз и, возможно, более одного раза. Поскольку каждый блок в таблице нужно будет читать, вы не сохраняете работу, выполняя индексный поиск - вы добавляете работу.

Когда вы переписываете цикл как один запрос, оптимизатор может принять это во внимание. Один из вариантов заключается в том, чтобы сделать объединение вложенного цикла, основанное на employee, которое будет по существу таким же, как явный цикл в PL/SQL (минус переключение контекста, как указано Mark). Однако, учитывая отношения между двумя таблицами и отсутствие предиката фильтрации, оптимизатор сможет сказать, что более эффективно просто полностью сканировать обе таблицы и объединить объединение или хеш-соединение. Это фактически приводит к меньшему количеству физических IO (предполагая чистый кэш в начале каждого выполнения) и значительно меньшее количество логических IO.

+0

Хороший ответ. И лучший ответ, чем я предусмотрел ... :-) –

4

«Основные процессы» требуют большого ответа. Я оставлю Tom Kyte, чтобы ответить на этот вопрос;)

Смежные вопросы