Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
БД / Uchpos / Gl_7.doc
Скачиваний:
25
Добавлен:
27.04.2015
Размер:
189.44 Кб
Скачать

Логическая оптимизация запросов

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

Поэтому достаточно трудно привести полную характеристику и классификацию методов логической оптимизации. Ограничимся несколькими примерами, а основную часть этого подраздела посвятим рассмотрению одного частного, но важного класса логических преобразований, касающихся сложных запросов, выраженных на языке SQL.

Очевидный класс логических преобразований составляют преобразования, связанные с приведением предикатов, задающих условие выборки в данном запросе, к каноническому представлению. Имеются в виду предикаты, содержащие операции сравнения простых значений. В общем случае такой предикат имеет вид "арифметическое выражение op арифметическое выражение", где арифметические выражения левой и правой частей в общем случае содержат имена полей отношений и константы (такие предикаты допускаются, например, в языке SQL, причем среди констант могут быть и литеральные константы, и имена переменных объемлющей программы, значения которых становятся известными только при реальном выполнении запроса).

Приведение предикатов к каноническому виду оправдано всегда, но сами канонические представления могут быть различными для предикатов, обладающих разными свойствами. Если предикат включает только одно имя поля, то его каноническое представление может, например, иметь вид "имя поля op константное арифметическое выражение" (это наиболее простая форма предиката - простой предикат селекции). Например, если начальное представление предиката имеет вид (a+5)*A op 36 (малыми буквами обозначены имена объемлющих переменных, а большими - имена полей отношений), то каноническим представлением такого предиката может быть A op 36/(a+5).

Если предикат включает в точности два имени поля разных отношений (или двух разных вхождений одного отношения), то его каноническое представление может иметь вид "имя поля op арифметическое выражение", где арифметическое выражение в правой части включает только константы и второе имя поля (это тоже достаточно простая и полезная форма для выполнения следующего шага оптимизации - предикат соединения; особенно важным случаем является случай эквисоединения, когда op - это равенство). Например, если в начальном представлении предикат имеет вид 5*A-a*B op b, то каноническим представлением может быть A op (b+a*B)/5.

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

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

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

При приведении логического условия к каноническому представлению можно производить поиск общих предикатов (они могут существовать изначально, могут появиться после приведения предикатов к каноническому виду или в процессе нормализации логического условия), и, кроме того, может быть произведено упрощение логического выражения за счет, например, выявления конъюнкции взаимно противоречащих предикатов. Так, если в логическом выражении встречается фрагмент ...(A>5)AND(A<5)..., то его можно заменить на ...FALSE... Возможны и более "умные" упрощения. Например, фрагмент логического выражения ...(A>B)AND(B=5)... можно заменить на ...(A>5)... Как видно из последнего примера, такие упрощения могут оказаться очень существенными для дальнейшей обработки запроса: в запросе с логическим условием первого вида предполагалось выполнение соединения двух отношений; после преобразования запрос уже не требует соединения.

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

- (A JOIN B) WHERE restriction-on-A AND restriction-on-B

эквивалентно выражению

(A WHERE restriction-on-A) JOIN (B WHERE restriction-on-B);

- (A WHERE restriction-1) WHERE restriction-2

эквивалентно выражению

A WHERE restriction-1 AND restriction-2;

- (A [attribute-list-1]) [attribute-list-2]

эквивалентно выражению

A [attribute-list-2];

- (A [attribute-list-1) WHERE restriction-1

эквивалентно выражению

(A WHERE restriction-1) [attribute-list-1].

Здесь JOIN обозначает реляционный оператор естественного соединения отношений; A WHERE restriction - оператор ограничения отношения A в соответствии с предикатом restriction (т.е. A WHERE restriction - это отношение, включающее кортежи, входящие в отношение A и удовлетворяющие предикату restriction); A [arrtibute-list] - проекция отношения A на заданный список атрибутов (т.е. A [attribute-list] - это отношение, состоящее из кортежей, каждый из которых получен выборкой указанных в списке полей из соответствующего кортежа отношения A, причем возможно появляющиеся кортежи-дубликаты уничтожены).

Хотя немногие реляционные системы имеют языки запросов, основанные в чистом виде на реляционной алгебре (примером такой системы может быть система SQUIRAL [23]), приведенные правила преобразований алгебраических выражений могут быть полезны и в других системах. Довольно часто реляционная алгебра используется в качестве основы внутреннего представления запроса, т.е. запрос в начальном представлении преобразуется к алгебраической форме, и следующие стадии оптимизации производятся над этим представлением. Естественно, что после этого можно выполнять и алгебраические преобразования.

В частности, существуют подходы, связанные с преобразованием к алгебраической форме запросов на языке SQL. Можно выявить две основные побудительные причины преобразований запросов на SQL к алгебраической форме. Первой причиной может быть стремление к использованию реляционной алгебры в качестве унифицированного внутреннего интерфейса реляционной СУБД. Особенно распространен такой подход при использовании специализированных машин баз данных, на основе которых реализуются различные интерфейсы доступа к базам данных. Тогда, естественно, интерфейс машины баз данных должен быть унифицирован (например, быть алгебраическим), а все остальные интерфейсы, включая интерфейс на основе SQL, приводятся к алгебраическому.

Более важной, особенно в контексте проблем оптимизации, причиной является то, что реляционная алгебра более проста, чем язык SQL. Поэтому, если запрос преобразован к алгебраической форме, дальнейшие действия оптимизатора по выборке оптимальных планов выполнения запроса становятся более простыми. Другими словами, вообще говоря, развитый оптимизатор запросов системы, ориентированной на SQL, должен выявить все возможные планы выполнения любого запроса, но при этом "пространство поиска" этих планов в общем случае очень велико, и в каждом конкретном оптимизаторе используются свои эвристики для сокращения пространства поиска. При этом некоторые потенциально возможные планы вообще никогда не будут рассматриваться (а они могут оказаться более оптимальными). Разумное преобразование запроса на SQL к алгебраическому представлению сокращает пространтво поиска планов выполнения запроса с гарантией того, что оптимальные планы потеряны не будут.

Основной особенностью языка SQL, отличающей его от языка реляционной алгебры, являются наличие возможности использовать в логическом условии выборки предикаты, содержащие вложенные подзапросы. При этом глубина вложенности не ограничивается языком, т.е., вообще говоря, может быть произвольной. Различные предикаты с вложенными подзапросами при наличии общего синтаксиса могут обладать весьма различной семантикой. Единственным общим для всех возможных семантик вложенных подзапросов алгоритмом выполнения запроса является вычисление вложенного подзапроса всякий раз при вычислении значения предиката. Естественно поэтому стремиться к такому преобразованию запроса, содержащего предикаты со вложенными подзапросами, которое раскроет семантику подзапроса, т.е. явно отобразит ее в синтаксисе преобразованной формы, предоставив тем самым в дальнейшем оптимизатору возможность выбрать способ выполнения запроса, наиболее соответствующий семантике подзапроса.

Будем использовать следующие обозначения: Ri - i-е отношение базы данных; Ck - k-е поле (столбец) отношения.

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

Простые предикаты. Это предикаты вида Ri.Ck op X, где X константа или список констант, и op - оператор скалярного сравнения (=, !=, >, >=, <, <=) или оператор проверки вхождения во множество (IS IN, IS NOT IN).

Предикаты со вложенными подзапросами. Это предикаты вида Ri.Ck op Q, где Q - блок запроса, а op может быть таким же, как для простых предикатов. Предикат может также иметь вид Q op Ri.Ck. В этом случае оператор принадлежности ко множеству заменяется на CONTAINS или DOES NOT CONTAIN. Очевидно, что эти две формы симметричны, так что достаточно рассматривать только одну. (В соответствии с принятыми при описании синтаксиса SQL правилами обозначениями нелитералов блоком запроса (query block) называется допустимая конструкция языка, начинающаяся с ключевого слова SELECT, т.е. в блоке запроса не допускаются теоретико-множественные конструкции с использованием UNION, INTERSECT и MINUS. )

Предикаты соединения. Это предикаты вида Ri.Ck op Rj.Cn, где Ri != Rj и op - оператор скалярного сравнения.

Предикаты деления. Это предикаты вида Qi op Qj, где Qi и Qj - блоки запросов, а op может быть оператором скалярного сравнения или оператором проверки вхождения в множество.

Приведенная классификация является некоторым упрощением реальной ситуации в SQL. Не рассматриваются предикаты соединения наиболее общего вида, включающие арифметические выражения с полями более чем двух отношений. Более того, для упрощения рассматриваются только соединения, в которых op - это операция равенства (эквисоединения).

Каноническим представлением запроса на n отношениях называется запрос, содержащий n-1 предикат соединения и не содержащий предикатов со вложенными подзапросами (классов 2 и 4). Фактически, каноническая форма - это алгебраическое представление запроса.

Пусть запрос имеет глубину вложенности подзапросов - 1, т.е. состоит из внешнего и одного или более вложенных блоков на одном уровне. Предположим также, что логическое условие, указанное в разделе WHERE запроса, содержит в точности один предикат класса 2 или 4. Считается, кроме того, что список выборки внутреннего блока состоит из одного элемента. Вводится дополнительная классификация предикатов с вложенными подзапросами класса 1.

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

SELECT Ri.Ck FROM Ri WHERE Ri.Cn =

SELECT MAX (Rj.Cm) FROM Rj WHERE Rj.Cl > a.

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

Предикаты со вложенными подзапросами типа N такие же, как предикаты типа A, но без агрегатной функции. Примером соответствующего запроса может быть

SELECT Ri.Ck FROM Ri WHERE Ri.Cn IS IN

SELECT Rj.Cm FROM Rj WHERE Rj.Cl > a.

Как и в предыдущем случае внутренний подзапрос может быть полностью вычислен независимо от внешнего с получением списка скалярных значений и сведением предиката к простому. Однако не всегда этот способ выполнения запроса является оптимальным (все зависит от размеров получаемого списка скалярных значений).

Внутренние подзапросы предикатов типа J содержат предикаты соединения с отношением внешнего блока и не содержат агрегатных функций. Пример использования предиката этого типа:

SELECT Ri.Ck FROM Ri WHERE Ri.Cn IS IN

SELECT Rj.Cm FROM Rj WHERE Rj.Cr = Ri.Ch AND Rj.Cl > a.

Наконец, у предикатов типа JA внутренний блок содержит предикат соединения с отношением внешнего запроса и в списке выборки указана агрегатная функция. Например:

SELECT Ri.Ck FROM Ri WHERE Ri.Cn =

SELECT AVG (Rj.Cm) FROM Rj WHERE

Rj.Cr = Ri.Ch AND Rj.Cl > a.

Среди предикатов класса 4 выделяется подкласс таких предикатов, вложенные блоки которых Qi или Qj (или и тот, и другой) содержат предикаты соединения с отношением внешнего запроса. Обозначим этот подкласс - D. Примером запроса с предикатом типа D может быть

SELECT Ri.Ck FROM Ri WHERE

(SELECT Rj.Cm FROM Rj WHERE Rj.Cn = Ri.Cl) CONTAINS

(SELECT Rp.Cm FROM Rp WHERE Rp.Cr > a).

Предикаты класса 4, не относящиеся к типу D, не представляют интереса, потому что такие предикаты полностью вычисляются независимо от внешнего запроса и могут быть заменены на логическую константу (конечно, во время реального выполнения запроса). Это естественный и наиболее эффективный способ выполнения запроса, содержащего такой предикат.

Предлагается набор алгоритмов, преобразующих запросы, содержащие предикаты со вложенными подзапросами типов N, J, JA и D, к канонической форме. Эти преобразования, по сути, раскрывают семантику запроса, скрытую за единообразным синтаксисом SQL, путем его приведения к алгебраической форме.

Приведение к канонической форме запросов с предикатами типов N и J, содержащих оператор проверки вхождения в множество IS IN основано на следующей лемме:

Пусть запрос Q1 - это

SELECT Ri.Ck FROM Ri, Rj WHERE Ri.Cn = Rj.Cm

и запрос Q2 - это

SELECT Ri.Ck FROM Ri WHERE Ri.Cn IS IN

SELECT Rj.Cm FROM Rj.

Тогда запросы Q1 и Q2 эквивалентны, т.е. дают одинаковый резульат.

При этом предполагается, что если каноническое представление запроса на n отношениях получено из запроса с N или J предикатами с операциями сравнения IS IN, то до выполнения соединений производится необходимая фильтрация и проецирование отношений, соответствующих внутреннему подзапросу с удалением возможных дубликатов. Только при таком выполнении преобразованного запроса его семантика будет совпадать с семантикой запроса в начальном виде.

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

Примером преобразования запроса с предикатом класса J к канонической форме может быть следующий:

SELECT Ri.Ck FROM Ri WHERE Ri.Ch IS IN

SELECT Rj.Cm FROM Rj WHERE Ri.Cn = Rj.Cp

эквивалентно

SELECT Ri.Ck FROM Ri, Rj WHERE

Ri.Ch = Rj.Cm AND Ri.Cn = Rj.Cp.

Однако не так просто обстоят дела с предикатами типов N и J, в которых используется оператор проверки невхождения во множество IS NOT IN. Например, запрос

SELECT Ri.Ck FROM Ri WHERE Ri.Ch IS NOT IN

SELECT Rj.Ch FROM Rj WHERE Rj.Cn =

a не эквивалентен запросу

SELECT Ri.Ck FROM Ri WHERE Ri.Ch IS IN

SELECT Rj.Ch FROM Rj WHERE Rj.Cn != Rj.Cp.

Потенциально лучшим способом выполнения такого запроса является выработка временного отношения X, соответствующего внутреннему подзапросу, сортировка обоих отношений по Ch и затем одновременное сканирование отсортированных файлов с тем, чтобы найти кортеж отношения Ri, не входящий в X. Можно сказать, что Ri и X "антисоединяются" предикатом анисоединения NOT (Ri.Ch = X.Ch). Если допустить наличие предикатов антисоединения в каноническом представлении запроса, то предыдущий запрос эквивалентен следующему:

SELECT Ri.Ck FROM Ri, X WHERE NOT (Ri.Ch = Rj.Ch).

При этом предикаты антисоединения при выполнении запроса должны вычисляться только после выполнения предикатов соединения, полученных из J-предикатов c IS NOT IN. Например, если запрос с J-предикатом

SELECT Ri.Ck FROM Ri WHERE Ri.Ch IS NOT IN

SELECT Rj.Cm FROM Rj WHERE Rj.Cn = Ri.Cp

преобразован к псевдоканонической форме

SELECT Ri.Ck FROM Ri, Rj WHERE

Ri.Cp = Rj.Cn AND NOT (Ri.Ch = Rj.Cm),

то для сохранения семантики запроса в его исходной формулировки преобразованный запрос нужно понимать следующим образом: для каждого очередного кортежа отношения Ri выполняется соединение с отношением Rj в соответствии с предикатом соединения. При этом производится подотношение R1j, которое заменяет Rj в предикате антисоединения.

Рассмотренные возможные преобразования обобщаются в следующем алгоритме NEST-N-J, преобразующем запросы с предикатами типов N и J к "канонической" форме (понятно, что ее можно считать только псевдоканонической, поскольку отсутствует единая семантика предикатов соединения):

Объединить все списки отношений, встречающихся во всех разделах FROM, в один список FROM.

Объединить через AND логические условия всех разделов WHERE в одно логическое условие.

Заменить предикаты вида Ri.Cn op (SELECT Rj.Cm) на предикаты Ri.Cn op Rj.Cm, если op отлично от IS IN и IS NOT IN; на предикаты Ri.Cn = Rj.Cm, если op - это IS IN; на предикаты NOT( Ri.Cn = Rj.Cm), если op - это IS NOT IN.

Оставить список выборки внешнего запроса.

Преобразования запросов с предикатами типа JA основывается на наблюдении, что запрос Q3

SELECT Ri.Ck FROM Ri WHERE Ri.Ch =

SELECT AGG (Rj.Cm) FROM Rj WHERE Rj.Cn = Ri.Cp

эквивалентен запросу Q4

SELECT Ri.Ck FROM Ri WHERE Ri.Ch =

SELECT Rt.C2) FROM Rt WHERE Rt.C1 = Ri.Cp, где Rt (C1, C2) =

SELECT Rj.Cn, AGG (Rj.Cm) FROM Rj GROUP BY Rj.Cn.

Поскольку запрос Q4 содержит предикат типа J, он может быть преобразован к канонической форме с помощью алгоритма NEST-N-J. В более общем случае для запроса вида

SELECT R1.Cn+2 FROM R1 WHERE R1.Cn+1 op

SELECT AGG (R2.Cn+1) FROM R2 WHERE

R2.C1 = R1.C1 AND ... AND R2.Cn=R1.Cn

можно применить алгоритм преобразования NEST-JA:

Генерируется временное отношение Rt(C1,..., Cn, Cn+1), соответствующее запросу

SELECT C1,..., Cn, AGG (Cn+1) FROM R2

GROUP BY C1,..., Cn.

Внутренний блок запроса в начальной форме преобразуется путем замены всех вхождений имен полей отношения R2 на соответствующие имена полей отношения Rt; идентификатор агрегатной функции удаляется.

Алгоритм естественным образом обобщается на случай произвольной глубины вложенности внутренних подзапросов.

Описанный алгоритм не является вполне корректным, поскольку искажает семантику запроса с предикатом, включающим агрегатную функцию COUNT. Например, запрос

SELECT Ri.Ck FROM Ri WHERE Ri.Ch =

SELECT COUNT (Rj.Cm) FROM Rj WHERE Rj.Cn = Ri.Cp

на самом деле не эквивалентен запросу

SELECT Ri.Ck FROM Ri, Rt WHERE

Ri.Ch = Rt.Cm AND Ri.Cp = Rt.Cn,

где Rt ( Cp, Cn ) = SELECT Rj.Cp, COUNT (Rj.Cn) FROM Rj

GROUP BY Rj.Cp.

Если для некоторого кортежа отношения Ri значение поля Ri.Ch равно 0, а значение поля Ri.Cp таково, что в отношении Rj нет ни одного кортежа с таким же значением поля Rj.Cn, то по запросу в исходной формулировке будет выдано значение Ri.Ck этого кортежа (поскольку по определению функции COUNT она принимает значение 0 на пустом отношении). Преобразованный же запрос не выдаст значение Ri.Ck для этого кортежа, поскольку предикат соединения будет ложным. Это еще одно соображение в пользу того, что семантика запросов при их преобразованиях должна выражаться более точно.

Преобразования запросов, содержащих предикаты типа D, основаны на следующей лемме:

Пусть Q5 есть запрос

SELECT Ri.Ck FROM Ri WHERE

(SELECT Rj.Ch FROM Rj WHERE Rj.Cn = Ri.Cp) op

(SELECT Rk.Cm FROM Rk), а Q6 - запрос

SELECT Ri.Ck FROM Ri WHERE Ri.Cp = (SELECT C1 FROM Rt),

где Rt (C1) определяется запросом

SELECT Rj.Cn FROM Rj Rx WHERE

(SELECT Rj.Ch FROM Rj RY WHERE RY.Cn = RX.Cn) op

(SELECT Rk.Cm FROM Rk).

Запросы Q5 и Q6 эквивалентны.

Заметим, что запрос Q6 содержит предикат типа N, и, следовательно, может быть преобразован к канонической форме. Запрос, определяющий отношение Rt, является формулировкой на SQL реляционной операции деления.

На лемме основан алгоритм NEST-D, приводящий запрос с предикатом типа D к канонической форме. Пусть задан запрос достаточно общего вида:

SELECT R1.C1 FROM R1 WHERE

(SELECT R2.C1 FROM R2 WHERE

R2.C2 = R1.C2 AND ... AND R2.Cn = R1.Cn) =

(SELECT R3.C1 FROM R3 WHERE

R3.C2 = R1.C2 AND ... AND R3.Cm = R1.Cm).

Предположим, что n > m. Тогда действие алгоритма NEST-D состоит в следующем:

Образуем два временных отношения Rt1 (C1, ..., Cm) и Rt2 (C1, ..., Cn). Rt1 определяется запросом

SELECT C1, ..., Cm FROM R3, а Rt2 - запросом

SELECT C1, ..., Cn FROM R2.

Обозначим через Rt3 (Cm+1, ..., Cn) временное отношение, являющееся частным от реляционного деления Rt2 на Rt1.

Преобразуем начальный запрос к каноническому виду. Для этого

3a) Исключим внутренний блок запроса с отношением R3. 3b) Заменим все ссылки на поля отношения R2 на ссылки на соответствующие поля отношения Rt3 во внутреннем блоке запроса с отношением R2. 3c) Уничтожим в этом блоке ключевые слова SELECT и FROM. 3d) Включим Rt3 в список раздела FROM внешнего запроса.

В результате работы этого алгоритма запрос будет преобразован к канонической форме

SELECT R1.C1 FROM R1, Rt3 WHERE

R1.Cm+1 = Rt3.Cm+1 AND ... AND R1.Cn = Rt3.Cn.

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

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

Соседние файлы в папке Uchpos