InterBase: тормозология и глюконавтика
Страница 11. Подзапросы


Подзапросы

Во-первых, для хорошего планировщика очевиден тот факт, что многие подзапросы могут быть преобразованы в соединения, и наоборот. Это открывает возможности для применения всех описанных выше методов оптимизации соединений. Однако только не в interbase. Здесь порядок отрабоки можно регулировать планом лишь в пределах одной фразы from. Зато практически никаких ограничений на планы внутри подзапроса нет. На основе вышеприведённой структуры можно привести следующие эквивалентные примеры:
/* 1 */ select z from t2, t1 where t2.x = t1.x and t1.y = 33;
/* 2 */ select z from t2 where x = some (select x from t1 where y = 33);
/* 3 */ select z from t2 where x in (select x from t1 where y = 33);
/* 4 */ select z from t2 where 33 = (select y from t1 where t1.x = t2.x);
/* 5 */ select z from t2 where exists( select * from t1 where t1.x = t2.x and t1.y = 33);
За исключением разве что вариантов 2 и 3 все эти запросы - разные с точки зрения interbase. Хотя на самом деле все пять - совершенно эквивалентные с точки зрения реляционной алгебры. Это, к стати, один из недостатков SQL - слишком много возможностей, чтобы запутать простые вещи. Отсюда мораль - если есть возможность, избавьтесь от подзапросов.

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

Некоррелированные запросы, наоборот, не зависят от охватывающего запроса. Они просто возвращают ему значение или набор значений. Значит, их нужно вычислять всего один раз. Это interbase понимает. В приведённых примерах подзапросы 2 и 3 являются некоррелированными, а подзапросы 4 и 5 - коррелированные.

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

Оказалось, что interbase умеет оптимизировать планы вложенных запросов с учётом охватывающего их контекста. Если вернуться к серии вышеприведённых примеров и рассмотреть запросы 2 или 3, то в них interbase для отработки подзапроса может использовать индексы t1(x) и t1(y). Или даже оба индекса сразу, так как уже было сказано, что поддерживается объединение и пересечение индексов через or или and.

Ну ладно, с y всё понятно - это поле фигурирует в where. А вот x - нет. И если подзапрос попытаться выполнить отдельно, то план с индексом по x так же не будет воспринят. Однако мы имеем дело именно с подзапросом. Который вызывается из охватывающего для поиска соответствия как раз по этому полю x. Причём подобные фокусы порой проходят и на довольно навороченных подзапросах с соединениями.

Однако же тоже не всегда. Если Вы напишете подзапрос, скажем, с having, который практически не поддаётся такому планированию "снаружи внутрь", то остаётся Вам только посочувствовать.

 

 
« Предыдущая статья