Программирование >>  Полное сканирование таблицы 

1 ... 28 29 30 [ 31 ] 32 33 34 ... 107


Если необходимо, можно подобным образом связать все соединения, чтобы полностью контролировать порядок соединения. Для каждого соединения, следующего за первым, добавьте к выражению соединения логически несущественный компонент, который ссылается на один из столбцов, добавленных в предыдущем соединении. Например, если вы хотите, чтобы обращения к таблицам с Т1 по Т5 шли по порядку, можно использовать следующий код. Обратите внимание, что в условии соединения для таблицы ТЗ используется выражение 0*Т2. Key2 ID, чтобы сначала было выполнено соединение с Т2. Подобным образом в условии соединения для таблицы Т4 используется 0*ТЗ. Key3 ID, чтобы сначала было выполнено соединение с ТЗ.

... AND Tl.Key2 ID=T2.Key2JD AND Tl.Key3 ID+0*T2.Key2 ID = ТЗ.КеуЗ 10 AND Tl.Key4 ID+0*T3.Key3 I0 = T4.Key4 ID AND Tl.Key4 I0+0*T4.Key4 ID = T5.Key5 ID ...

Я воспользуюсь этим методом на конкретном примере. Возьмем из главы 3 следующий SQL-код.

SELECT E.First Name. Е. Lastjame. E.Sal ary. LE. Descri pti on.

M.Fi rstjame. M. Lastjame. LM. Description FROM Locations LE. Locations LM. Employees M. Employees E WHERE E. Last Jame = Johnson

AND E.Manager ID = M.Employee ID

AND E.Location ID = LE.LocationJD

AND M.Location ID = LM.Location IO

AND LE.Description = Dallas

Предположим, что у нас есть план выполнения, который начинает с индекса по фамилии сотрудников, но вы обнаружили, что соединение с местоположением сотрудников (псевдоним LE), чтобы отбросить всех сотрудников, не находящихся в Далласе (Dal 1 as), к сожалению, происходит в последнюю очередь, после остальных соединений (с М и LM). Необходимо присоединить LE сразу же после Е, чтобы минимизировать количество строк, которое потребуется соединить с остальными двумя таблицами. Если начать с Е, то сразу же вьшолнить соединение с LM невозможно, поэтому если вы запретите соединение с М перед LE, то должны получить желаемый порядок соединения. Как это сделать:

SELECT Е. First Jame. Е.Last Jame. Е.Salary. LE. Descri pti on.

M.Fi rstjame. M. Lastjame. LM. Descri pti on FROM Locations LE. Locations LM, Employees M. Employees E WHERE E. Last Jame = Johnson

AND E.Manager ID + 0*LE.Location ID=M.Employee ID

AND E.LocationJD = LE.LocationJD

AND M.LocationJD = LM.LocationJD

AND LE.Description = Dallas

Смысл в том, что я сделал соединение с М зависящим от значения из LE. Выражение 0*LE.Location ID заставляет оптимизатор вьшолнить соединение с LE до М. Благодаря умножению на ноль добавленное выражение не влияет на результаты, возвращенные запросом.

Выбор порядка выполнения для внешних запросов и подзапросов

Большинство запросов с подзапросами могут логически начинаться с внешнего запроса или подзапроса. В зависимости от селективности условия подзапроса лю-



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

SELECT ...

FROM Departments D

WHERE EXISTS (SELECT NULL FROM Employees E

WHERE E.Department ID=D.Department ID)

можно преобразовать в следующий код:

SELECT ...

FROM Departments D

WHERE D.Department ID IN (SELECT E.Department ID FROM Employees E)

Первая форма подразумевает, что база данных начинает с внешнего запроса и переходит к подзапросу. Для каждой строки, возвращенной внешним запросом, база данных выполняет соединение в подзапросе. Вторая форма подразумевает, что выполнение начинается со списка различных отделов, в которых существуют сотрудники, найденные подзапросом, и переходит от этого списка к соответствующему списку отделов во внешнем запросе. Иногда база данных сама использует подразумеваемый порядок соединения, хотя некоторые базы данных могут неявно выполнять преобразование, если оптимизатор обнаружит, что альтернативный порядок лучше. Чтобы сделать SQL-код более удобочитаемым и заставить его хорошо работать вне зависимости от того, может ли ваша база данных преобразовывать формы, используйте ту форму записи запроса, которая явно указывает желаемый порядок установки соединений. Чтобы этот порядок выполнялся, даже если база данных может провести преобразование, используйте те же техники выбора направления соединения, которые были описаны в разделе Запрещение соединения в неправильном порядке . Так, условие EXISTS, которое заставляет внешний запрос выполняться первым, будет выглядеть так:

SELECT ...

FROM Departments D

WHERE EXISTS (SELECT NULL FROM Erployees E

WHERE E.Department ID=D.Department ID+0)

Для противоположного порядка условие IN, заставляющее использовать другой порядок, от подзапроса к внешнему запросу, будет выглядеть тшс:

SELECT ...

FROM Departments D

WHERE D.Department ID IN (SELECT E.Department ID+0 FROM Employees E)

ПРИМЕЧАНИЕ

Второй порядок соединения чаще всего будет работать плохо, за исключением необычных случаев, когда отделов больше, чем сотрудников!

У вас может быть несколько подзапросов, в которых база данных должна переходить от внешнего запроса к подзапросу (например, для подзапросов NDT EXISTS) или же ей следует придерживаться стандартного порядка. Такой случай подразумевает выбор порядка выполнения подзапросов. Также вы можете оказаться перед выбором - выполнять ли подзапросы после завершения внешнего запроса, при первой же возможности выполнить корреляционное соединение, либо принять некое промежуточное решение.



Первая тактика управления порядком выполнения подзапросов - просто перечислить их по порядку во фразе WHERE (то есть верхний подзапрос будет выполнен первым). Это один из редких случаев, когда порядок во фразе WHERE имеет смысл.

Иногда база данных выполняет подзапрос раньше, чем вам хотелось бы. Для коррелированных соединений - соединений в подзапросах, которые связывают подзапросы с внешними запросами, - работает та же тактика: просто откладывать соединения (см. раздел Запрещение соединения в неправильном порядке ). Например, рассмотрим запрос: SELECT ...

FROM Orders 0. Customers С, Regions R WHERE O.Status Code = DP AND O.Customer IO = C.Customer ID AND C.Customer Type Code = GOV AND C.RegionJO = R.RegionJD AND EXISTS (SELECT NULL

FROM Order Details OD WHERE O.Order ID = OO.Order IO AND OD.Shipped Flag = У)

Для этого запроса вы можете обнаружить, что подзапрос выполняется сразу же, как только достигается соединение с ведущей таблицей Orders. Однако вы хотите, чтобы до того, как потратиться на выполнение подзапроса, было выполнено соединение с Customers и отброшены неправительственные заказы. В таком случае для того, чтобы отложить корреляционное соединение, потребуются следующие изменения кода: SELECT ...

FROM Orders 0. Customers С. Regions R WHERE O.Status Code = OP

AND O.CustomerJO = C.Customer ID

AND C.Customer Type Code = GOV

AND C.Region ID = R.Region ID

AND EXISTS (SELECT NULL

FROM Order Details OD

WHERE O.DrderJD + 0*C.CustOfner ID = OD.OrderJD AND OO.ShippedJlag = Y)

Обратите внимание на добавление +0*С. Customer ID к разделу WHERE подзапроса. Это гарантирует, что соединение с Customers будет выполнено первым, до вьшолнения подзапроса.

Предоставление стоимостному оптимизатору хороших данных

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



1 ... 28 29 30 [ 31 ] 32 33 34 ... 107

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика