Программирование баз данных MS SQL Server (1084479), страница 65
Текст из файла (страница 65)
Запросы с вложенными подзапросами, которые возвращают несколько значений По-видимому, наиболее широко применяются такие подзапросы, в которых в той или иной форме осуществляется выборка перечня допустимых значений, которые затем используются в запросе в качестве критериев. Для ознакомления с примером такого применения вложенного подзапроса переключимся на использование базы данных РОЬя (см. главу 5). Предположим, что требуется составить список всех магазинов, в которых покупателям предоставляются скидки. Напомним, что информация о магазинах хранится в таблице Ясогея, а сведения о скидках приведены в таблице Оьясоппся.
Предположим, что вначале для решения этой задачи применяется следующий запрос ОЯЕ НОВЯ ЯЕЬЕОТ ягог гб АЯ "Ясоге 10", ясог пате АЯ "Ясоге Мате" ГНОН Ягогея КНЕНЕ ясог 1б 1Б )ЯЕЬЕОТ ясог 1б ГНОИ 01яооппся) Как оказалось, выполнение этого запроса влечет за собой получение только одной строки, но любопытно то, что полученные результаты полностью совпадают с результатами выполнения внутреннего соединения в запросе, приведенном в главе 5; Яооге гп Ягоге Мате 8042 ВоогЬеяо )1 гоя)я) аггесгеб) 256 Глава 7 И действительно, запросы с вложенными подзапросами, подобные приведенному выше, почти всегда можно отнести к категории таких запросов, которые можно осуществить с использованием внутреннего соединения, а не вложенного оператора БЕЗВЕСТ.
Например, такие же результаты, как и в предыдущем запросе с вложенным подзапросом, можно получить после вызова на выполнение следующего простого оператора с конструкцией ЯОТМ: БЕЬЕСТ я.ягог Та АЯ "Бгоге 1О", ясог паве АЯ "Ясоге Няве" ТНОМ Ясогея я 101Н Озяооппгя а ОН я.ясог Та = о.ясог Ы В целях обеспечения высокой производительности следует неизменно прибегать к использованию метода, основанного на соединении, если нет каких-либо весомых аргументов в пользу применения запроса с вложенным оператором БЕЗВЕСТ. Дополнительная информация на эту тему приведена в конце настоящей главы.
Фактически в СУБД 5~Е. 5егоег предусмот~>сна возможность успешно справиться самостоятельно с решением этой задачи. В подавляющем большинстве ситуаций в СУБД АД 5езист пфименительно к запросу с вложенным подзапросозч действительно создается такой же план выполкения запРоса, котоРый использовался бы и для запроса с оператоРан соединекия. Поэтому, учитьшая данное обстоятельство, следует откровенно подчеркнуть, что чаще всего не набгюдагтся какое-либо различие в производипмльностли при сравнении опфаторов с вложенными подзап~осами и оператоуюв с соединениями. Проблема заключается не в том, какие действия по выполнению запРоса осущесгавляет СУБД, а в там„что уже неоднократно было сказано в этой книге.
Если оказываетаз, что в СУБД для запросов того и другого типа используются розные планы выполнения запросов, то обычно обнаруживаетсц что вариант запРоса с соединением обзадает лучшими хаРактеристикоми, поэтому Рскомвндуппся по умолчанию всегда использовать именно такие синтаксические конструкции. Использование запроса с вложенным оператором ьйтлСтдля поиска висячих строк Вложенный оператор БЕЗВЕСТ такого типа, который рассматривается в данном разделе, почти идентичен приведенному в предыдущем примере, за исключением того, что в нем используется также операция НОТ.
Переходя к сравнению с синтаксической конструкцией соединения, можно отметить, что из-за этого различия применяемый оператор с вложенным подзапросом начинает в большей степени напоминать внешнее соединение, а не внутреннее. Прежде чем перейти к изучению синтаксической конструкции искомого оператора с вложенным оператором ЯЕЕЕСТ, еще раз рассмотрим один из примеров внешнего соединения, приведенного в главе 5. В этом запросе предпринималась попытка выяснить, каким данным о магазинах из базы данных РпЪЯ не соответствуют какие- либо строки с данными о скидках: ОЯЕ РпЬя БЕЬЕСТ я.эгог паве АЯ "Бгоге паве" РЕОМ Оьяооопся а Н16НТ ООТЕН З01Н Бгогея я ОН Й.ЯГог 1О = я.згог 1О ПЕННЕ О.БГог 1О 1Я НООЬ В результате были получены следующие пять строк: Дополнительные сведения о запросах 257 Яяаге Нате Ег1с Све Веаб Воохя Вагпот'я Хеея 6 Вгеея Вос-П-Мас: Опа1гяу Ьаппбгу апб Воска Еггсаягке Воояяпор (5 гое(я) аттессеб) Если речь идет о том, какие операторы должны, как правило, использоваться для решения указанной задачи, то основная рекомендация состоит в применении именно таких операторов.
Но автор не может со всей уверенностью утверждать, что эта рекомендация действительно всегда применяется на практике. Вообще говоря, чтобы составить оператор с соединением, требуется предпринять более значительные умственные усилия, поэтому разработчики чаще всего вместо этого прибегают к использованию вложенных операторов ВЕРСТ. Автор предлагает попытаться составить такой запрос с вложенным оператором ЯЕЬЕСТ самостоятельно, но должен предупредить читателя о том, что в ходе этого возникнет определенный нюанс.
Закончив выполнение этой работы, ознакомьтесь с вариантом, предлагаемым автором. По моему мнению, такой запрос должен выглядеть следующим образом: ЯЕЬЕСТ яяог 1б АЯ "Яяоге 1П", яяог пате АЯ "Яяоге Хате" ГНОМ Яяогея ИНЕВЕ ясог гб ХОТ 1Х (ЯКЬКст яяог гб ГНОМ Втясоопяя ИНЕВЕ ясог гб 1Я ХОТ ХПЬЫ Выполнение этого запроса приводит к получению точно таких же пяти строк.
Но я полагаю, что при первой попытке читатель не предусмотрел операцию сравнения 1Я ХОТ ХОЬЬ во внутреннем запросе. При решении подобной задачи необходимость включать или не включать операцию проверки 1Я ХОТ ИОЬЬ зависит от того, допускается ли в рассматриваемом столбце применение Тес)Е(:значений, и от того, какие именно результаты требуется получить. Если в данном случае указанная операция сравнения не будет предусмотрена, то в конечном итоге сформируются неправильные результаты, на основании которых будет сделан ошибочный вывод, что нет таких магазинов, в которых не предоставляются скидки (тогда как в действительности такие магазины есть).
Причина этого связана с тем, как осуществляется сравнение со МЛЕТ;значениями. Вообще говоря, если есть возможность появления ЬПЗЕЕ-значений в сшлске 1Х, то при подготовке запроса необходимо соблюдать предельную осмотрительность. Связанные подзапросы Прежде чем приступить к изложению темы настоящего раздела, автор хочет высказать настоятельную рекомендацию — обратите особое внимание на то, что здесь сказано( Обычно все разработчики обладают каким-то стандартным набором навыков, но тот, кто знает, как действуют связанные подзапросы и как они применяются, имеет в своем распоряжении гораздо более широкие возможности. Говоря о том, как важно изучить эту тему, автор стремится также подчеркнуть, что важно понять практическую значимость связанных подзапросов.
258 Глава 7 Связанные подзапросы относятся к категории таких инструментов, с помощью которых можно сделать невозможное возможным. Более того, с помощью связанных подзапросов часто можно превратить несколько строк кода в одну и добиться соответствующего повышения производительности. Но сложность освоения данной темы обусловлена тем, что вначале требуется овладеть принципиально иным стилем мышления по сравнению с тем, который обычно применяется в программировании. На первгяй взгляд кажется, что связанные подзапросы относятся к категории наиболее простых в изучении и освоении понятий языка ВЯЕ.
Но после того как процесс изучения заканчивается, разработчик напрочь о них забывает, причем лишь потому, что сами понятия, лежащие в основе связанных подзапросов, противоречат складу его ума. Если же читатель относится к числу тех немногих счастливцев, которым удается закрепить в памяти принципы действия связанных подзапросов, то он, безусловно, становится также одним из немногих, которые способны решать задачи, которые кажутся не поддающимися решению. А когда придется бороться за каждую миллисекунду, чтобы повысить производительность запросов, то именно вы будете иметь в своем распоряжении гораздо более полный комплект инструментальных средств по сравнению с другими разработчиками.
Принципы работы связанных подзапросов Как было описано выше, вложенные подзапросы выполняются как однонаправленные, а при выполнении связанных подзапросов, в отличие от вложенных подзапросов, информация движется в обоих направлениях, а не в одном. Внутренний запрос в операторе с вложенным подзапросом обрабатывается только один рзз, после чего информация передается во внешний запрос, который также выполняется лишь одиножды. По существу, при этом с помощью внутреннего запроса вырабатывается единственное значение (или список значений), которое вполне можно было бы подставить во внешний запрос, введя его вручную.
С другой стороны, при выполнении связанных подзапросов внутренний запрос действует на основании информации, предоставленной внешним запросом, и наоборот. На первый взгляд такая организация работы может показаться не совсем понятной (снова возникает проблема "курицы" или "яйца"), но фактически осуществляется описанный ниже трехэтапный процесс.
0 Внешний запрос получает строку, и эта строка передается во внутренний запрос. С) Внутренний запрос выполняется с учетом переданного в него значения (значений). 0 Затем внутренний запрос передает во внешний запрос значения, сформированные из полученных в нем результатов, а во внешнем запросе эти значения используются для завершения намеченной в нем обработки. Использование связанных подзапросов в конструкции ЯНЕЗЕ Автор признает, что приведенного выше описания может быть недостаточно, поэтому рассмотрим применение связанных подзапросов на примерах. Дополнительные сведения о запросах 259 Вернемся к использованию базы данных Могг))ытпс) и снова займемся составлением запроса, позволяющего узнать, какие заказы были введены в ту первую дату, за которую имеется информация о заказах, введенных в систему. Однако, предположим, что на этот раз необходимо учесть дополнительное требование — определить значения Огс)ег10 и Огбе Рабе первого заказа, поступившего в систему от каждого заказчика.
Иными словами, требуется определить, когда впервые каждый из заказчиков разместил в компании свой заказ и какие идентификаторы имеют эти заказы. Рассмотрим решение этой задачи последовательно. Прежде всего для получения каждого набора искомых результатов необходимо определить значения Огс)егРаге, Огс)ег10 и Сцзгоп)ег10. Всю эту информацию можно найти в таблице Огс)егэ, поэтому можно сделать вывод, что будущий запрос должен быть хотя бы отчасти основан на этой таблице. Затем отметим, что для каждого заказчика необходимо определить, когда в системе впервые появились его заказы.