|
Программирование >> Программирование баз данных
29482 49746 2003-03-22 00:00:00.000 29483 49665 2003-03-13 00:00:00.000 (19134 row(s) affected) Как уже быяо оказано, при выполнении того же сценария на компьютере читателя могут быть получены результаты, отличные от приведенных в этом примере. Причина этюго может быть связана с тем, что вы провели с данными базы данных Adven -tureWorks либо больше, либо меньше экспериментов, чем автор. Итак, в данном случае формируются два полностью отдельных результирующих набора. На это указывает тот факт, что в результатах присутствуют две разные строки с указанием количества затронутых строк, row (s) affected. Но такая организация работы чаще всего отрицательно влияет на производительность. Этот вопрос будет рассматриваться более подробно в настоящей главе после описания других способов решения данной задачи. Однако следует отметить, что такой подход, основанный на использовании двух запросов, иногда становится единственным способом добиться поставленной цели без применения курсора, но рассматриваемая задача не относится к такой категории. Итак, было бы желательно объединить два приведенных выше запроса в один, но для этого необходимо найти способ поиска информации о каждом заказчике. Этой цели можно достичь, используя внутренний запрос, который выполняет поиск с учетом текущего значения CustomerlD, полученного из внешнего запроса. После этого необходимо снова вернуть результат внутреннего запроса во внешний запрос, чтобы можно было выполнить выборку требуемых строк по данным о самой ранней дате заказа. Оператор, созданный на основании изложенных требований, может выглядеть примерно так: SELECT о1.CustomerlD, о1.SalesOrderlD, о1.OrderDate FROM Sales.SalesOrderHeader ol WHERE ol.OrderDate = (SELECT MIN(o2.OrderDate) FROM Sales.SalesOrderHeader o2 WHERE o2.CustomerlD = ol.CustomerlD) ORDER BY CustomerlD Применение этого оператора приводит к получению тех же 19 134 строк. В этом запросе заслуживает внимания несколько описанных ниже важных особенностей. В результатах обнаруживается только одно сообщение о количестве затронутых строк, row (s) affected. На этом основании можно с уверенностью сказать, что для вьшолнения был намечен только один план запроса. Внешний запрос (в данном примере) весьма напоминает по внешнему виду вложенный подзапрос, а внутренний запрос имеет явно определенную ссылку на внешний запрос (обратите внимание на использование псевдонима о1 ). Псевдонимы применяются в обоих запросах (даже несмотря на то, что на первый взгляд во внешнем запросе псевдоним не требуется), поскольку псевдонимы необходимы для каждого явного обращения к столбцу из внешнего или внутреннего запроса (при этом либо внутренний запрос обращается к столбцу внешнего запроса, либо наоборот). Но истина заключается в том, что псевдонимы иногда действительно нужны, а иногда нет. Автор обычно не использовал псевдонимы во всех типах вложенных подзапросов, которые рассматривались в начальных разделах этой главы, а что касается связанных подзапросов, то в них неизменно применяются псевдонимы. Существует надежное и простое правило - псевдоним необходимо предусматривать для любой таблищ! (и относящихся к ней столбцов), на которую должна быть сформирована ссылка из внешнего запроса. Однако проблема заключается в том, что при попытке составить запрос, руководствуясь этим правилом, может очень быстро возникнуть путаница в отношении того, для чего нужен псевдоним, а для чего нет. Поэтому самый оправданный подход состоит в том, чтобы предусматривать псевдонимы для всех таблиц. Это позволяет уверенно разбираться в том, из какой таблицы какого запроса поступает искомая информация. В рассматриваемом примере сообщение 19134 row (s) affected появилось только один раз. Это связано с тем, что в запросе 19 134 строки действительно были затронуты только единожды. Даже на основании только этого наблюдения можно сделать вывод, что последняя версия с одним запросом должна, по-видимому, обладать более высоким быстродействием, чем версия с двумя запросами. Так оно и обстоит в действительности. Дополнительная информация на эту тему также будет приведена ниже. В данном конкретном запросе внешний запрос ссылается на внутренний запрос в конструкции WHERE. Во внешнем запросе реализована также возможность запросить данные из внутреннего запроса для включения в список выборки. При обычных обстоятельствах право использовать или не использовать псевдоним остается за разработчиком, а когда речь идет о связанных подзапросах, применение псевдонимов часто становится обязательным. Данный конкретный запрос представляет собой весьма наглядный пример, показывающий, чем обусловлена такая необходимость, поскольку в нем и внутренний, и внешний запросы основаны на одной и той же таблице. Очевидно, что оба запроса получают информацию друг от друга, поэтому без псевдонимов невозможно было бы указать, какой экземпляр данных таблицы представляет интерес в том и в другом случае. Связанные подзапросы в списке выборки Подзапросы могут также использоваться для получения в виде результатов выборки ответов немного другого рода. Необходимость в этом часто возникает, если искомая информация принципиально отличается по своему составу от остальной части данных, рассматриваемых в запросе (например, если необходимо выполнить агрегирование данных по одному столбцу, но желательно при этом избежать влияния выполнения операции агрегирования на результаты выборки данных из других столбцов). В качестве примера подзапроса такого типа рассмотрим немного модифицированную версию запроса, который использовался в последнем разделе. Но в данном случае требуется определить только имена заказчиков и первую дату получения заказа от каждого из заказчиков. Для составления требуемого запроса придется внести более значительные изменения, чем может показаться на первый взглад. Прежде всего, теперь в результатах должно присутствовать имя заказчика, а это означает, что в запрос необходимо включить таблицу Customers. Кроме того, больше не требуется включать в запрос какое-либо условие, поскольку должны быть пол)ены данные по всем заказчикам (без исключения), и достаточно лишь узнать, какова дата получения первого заказа от этих заказчиков. Применение связанного подзапроса позволяет составить немного более простой запрос, чем тот, который использовался перед этим для решения указанной задачи: SELECT С.LastName, (SELECT MIN(OrderDate) FROM Sales.SalesOrderHeader о WHERE o.ContactID = e.ContactID) AS Order Date FROM Person.Contact с Результаты выполнения этого запроса приведены ниже. LastName Order Date Achong 2001-09-01 00:00:00.000 Abel 2003-09-01 00:00:00.000 Abercrombie 2001-09-01 00:00:00.000 Acevedo 2001-09-01 00:00:00.000 He 2004-04-12 00:00:00.000 Zheng 2004-02-15 00:00:00.000 Hu 2003-11-17 00:00:00.000 (19972 row(s) affected) В приведенных выше сокращенных результатах это не показано, но просмотр всего объема полученных данных позволяет обнаружить большое количество строк, содержащих NULL-значения в столбце Order Date. С чем, по мнению читателя, это связано? Безусловно, причина появления NULL-значений состоит в том, что отсутствует строка в таблице SalesOrderHeader, соответствующая строке таблицы Customers, рассматриваемой как текущая (во внешнем запросе). В связи с описанной ситуацией целесообразно немного отвлечься, чтобы рассмотреть чрезвычайно полезную функцию, позволяющую распознавать наличие NULL-значений, - ISNULL() . Обработка данных, содержащих NULL-значения, с помощью функции isnull В действительности имеется не одна, а несколько функций, специально предназначенных для обработки данных, содержащих NULL-значения, а в рассматриваемой ситуации целесообразно воспользоваться чрезвычайно полезной функцией такого типа- ISNULL (). Функция ISNULL () принимает в качестве параметра переменную или выражение, а затем проверяет, не имеет ли этот параметр неопределенное значение. Если параметр действительно имеет NULL-значение, то функция ISNULL () возвращает некоторое другое заранее заданное значение. Если же параметр функции имеет значение, отличное от NULL, то функция возвращает именно это значение. Синтаксис вызова функции ISNULL () довольно простой: ISNULL(<expression to test>, <replacement value if null>) Примеры результатов, полученных при вызове функции ISNULL (), приведены в табл. 6.L
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |