Без названия и без подзаголовка

Порядок соединения – Часть 1

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

select
	/*+ qb_name(main) */
	t1.v1
from
	t1, t3
where
	t1.n2 = 100
and	t3.n1 = t1.n1
and	t3.n2 = 100
and	exists (
		select
			/*+ qb_name(sub2) */
			t2.id
		from	t2
		where	t2.n1 = 15
		and	t2.id = t1.id
	)
and	exists (
		select
			/*+ qb_name(sub4) */
			t4.id
		from	t4
		where	t4.n1 = 15
		and	t4.id = t3.id
	)
;

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

В частности, способность Oracle устранять уровни вложенности (UNNEST) может значительно отличаться в разных версиях, а также может серьезно зависеть от вида общего запроса. Далее я буду изменять EXISTS на NOT EXISTS, или AND на OR как того будут требовать нить повествования.

Количество возможных вариантов выполнения и комментарии, которые идут за ними, настолько великолепны что я собираюсь опубликовать серию серьезных «глав», посвященных этому запросу. Сделать их самостоятельными страницами и разместить ссылки в главном меню. Главу которую вы сейчас читаете — будет первой.

Простые объединения и подзапросы (Simple Joins and Subqueries)

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

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

Для примера, если t1 — это таблица содержимого заказов, то t3 может быть таблицей самих заказов, а t2 (в первом подзапросе) может быть таблицей накладных, которая логически соотносится с таблицей содержимого заказов, а не с таблицей заказов.

Возникает вопрос. Если вы идентифицировали список содержимого заказа, должны ли вы объединить его с таблице заказов, только потому что эта таблица содержит дату, которая исключит 90% всех записей списка содержимого, который, кстати, вы только что сформировали? Или вы должны выполнить подзапрос к таблице накладных, потому что он исключит 99% из списка содержимого заказов?

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

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

Хинтование (Hinting)

В случаях, похожих на рассматриваемый, Oracle дает нам возможность некоторого стратегического контроля. Подсказки push_subq, no_push_subq, unnest и nounnest могут использоваться для указания основной стратегии выполнения запроса.

В 10g, подсказки push_subq должны размещаться внутри подзапросов, которые вы хотите обработать раньше. В предыдущих версиях Oracle они размещались во внешнем запросе. Обратите внимание на этот факт. Если вы уже используете push_subq в 8i или 9i — после обновления до 10g может показаться, что Oracle игнорирует вашу подсказку.

Оригинал: Join Ordering — Part 1