InterBase: тормозология и глюконавтика
Страница 15. Как написать запрос с планом. Часть 2


Ещё хуже ситуация, если к указанному запросу приписать order by t1.x. Очевидно, что этот порядок следует из индекса i_t1_x_y. И ни при первом, ни даже при втором плане делать ничего больше не нужно - просто выдать записи клиенту. Но interbase, опять же это понять не в состоянии. Он будет пытаться сортировать полученный результат и если памяти не хватит, то создаст временный файл. И до самого завершения "сортировки" клиент ни одной записи не увидит.

Сразу же получить нужную сортировку можно планом plan join (t1 order i_t1_x_y, t2 index (i_t2_x)). То есть таблица t1 сразу будет выбрана в нужном порядке, но t2 к ней будет подсоединяться не слиянием, как и в предыдущем плане. Это, правда, не критично, если подходящих записей в t1 мало или если клиент выбирает лишь первые из них (скажем, для грида).

Теперь о присоединяемых таблицах. При двухэтапном подходе (выбор ключей, а затем полных записей) они однозначно уходят на второй этап. Здесь вполне допустим обычный индексный поиск ( join (главная таблица ..., присоединённая index (rdb$primarynnn)). Если это справочник и он небольшого размера, то проблем не будет при любой форме запроса. Как правило, бывает именно так. Но иногда могут возникать проблемы и здесь придётся обрабатывать такую таблицу примерно теми же методами, что и основную, как описано выше.

Большие неприятности создаются внешними соединениями. Если написать from t1 left outer join t2 on y = z, то оптимизатор interbase по своей глупой природе имеет маниакальную привычку: просканировать сначала t1 natural, t2 index (i_t2_z), и лишь потом поверх этого отработать остальные условия в процессе перебора. Практика показывает, что подсунуть что-либо существенно иное практически невозможно. Если кто-нибудь раскопает методику, как заставить его делать хотя бы слияние, буду крайне благодарен. Едиснтвенное, что можно бывает сделать, это "пропихнуть" часть условий в сканирование "внешней" таблицы, в данном случае - t1. То есть указать руками, что фильтроваться через t1 index (...). А уж если после этого нужно упорядочение, то сортировки записей избежать практически невозможно.

Представлений следует по возможности избегать. Как ни печально это звучит, но это так. Особенно если в них есть distinct или внешние соединения. В таких случаях interbase будет упираться всеми силами, чтобы не взять нормальные планы, причём не давая вразумительных объяснений. Что именно не пройдёт можно надёжно выяснить только экспериментом в Interactive SQL. Если же представление всё же приходится использовать, то нужно ссылаться на входящие в него таблицы по их псевдонимам. Пример приведён выше.

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

И в заключение: если не видите эффективного решения, не бойтесь применять sort merge. Эта, казалось бы, страшная операция (как же, обе таблицы пересортировать, или сколько их там не соединилось), иногда оказывается самой быстрой! Например, если нужно соединить две большие таблицы, а индексов по нужным полям нет. Если придётся сортировать 100 тыс. записей для показа в гриде всего 10 из них, то это, конечно, плохо. Но если это же нужно для выборки 20 тыс., то не так уж.

Правила для повышения эффективности здесь примерно те же, что и для любых сортировок - максимально отфильтровать множества, сократить размеры записей до минимума, а остальное присоединить потом. То есть желательно, чтобы в sort merge участвовали только поля, по которым идёт соединение и ключи. И только потом к результатам слияния подсоединить справочники, или что там ещё понадобится. Ведь 20 тыс. записей, скажем, по 50 байт - это всего 1 МБ. Не так уж много для современного сервера.

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