Мартин Грубер - Понимание SQL (991940), страница 15
Текст из файла (страница 15)
Таким образом псевдоним опускает все, кроме заказчиков продавца Serres. Псевдоним b будет верным для всех строк с тем же самымзначением города что и текущее значение города для a; в ходе запроса, строка псевдонима b будет верна один раз когда значение города представлено в a.Нахождение этих строк псевдонима b — единственая цель псевдонима a, поэтоиму мы не выбираем все столбцы подряд.
Как вы можете видеть, собственные заказчики Serres выбираются при нахождении их в том же самом городе что и он сам,поэтому выбор их из псевдонима a необязателен. Короче говоря, псевдоним назходитстроки заказчиков Serres, Liu и Grass. Псевдоним b находит всех заказчиков размещенных в любом из их городов (San Jose и Berlin соответственно) включая, конечно,самих — Liu и Grass.Вы можете также создать обьединение которое включает и различные таблицыи псевдонимы одиночной таблицы. Следующий запрос объединяет таблицу Пользователей с собой: чтобы найти все пары заказчиков обслуживаемых одним продавцом.В то же самое время, этот запрос объединяет заказчика с таблицей Продавцов с именем этого продавца (вывод показан на Рисунке 9.5):SELECT sname, Salespeople.snum, first.cname, second.cnameFROM Customers first, Customers second, SalespeopleWHERE first.snum = second.snumAND Salespeople.snum = first.snumAND first.cnum < second.cnum;=============== SQL Execution Log ==================| SELECT cname, Salespeople.snum, first.cname|| second.cname|| FROM Customers first, Customers second, Salespeople || WHERE first.snum = second.snum|| AND Salespeople.snum = first.snum|| AND first.cnum < second.cnum;|| ====================================================|| cnamesnumcnamecname|| ------------------------|| Serres1002LiuGrass|| Peel1001HoffmanClemens|=====================================================Рисунок 9.5: Объединение таблицы с собой и с другой таблицейРЕЗЮМЕТеперь Вы понимаете возможности объединения и можете использовать их дляограничения связей с таблицей, между различными таблицами, или в обоих случаях.Вы могли видеть некоторые возможности объединения при использовании его способностей.
Вы теперь познакомились с терминами порядковые переменные, корреляционные переменные и предложения (эта терминология будет меняться от изделия кизделию, так что мы предлагаем Вам познакомится со всеми тремя терминами). Кроме того Вы поняли, немного, как в действительности работают запросы.Следующим шагом после комбинации многочисленых таблиц или многочисленых копий одной таблицы в запросе, будет комбинация многочисленных запросов, гдеодин запрос будет производить вывод который будет затем управлять работой другого запроса. Это другое мощное средство SQL, о котором мы расскажем в Главе 10 иболее тщательно в последующих главах.РАБОТА С SQL1. Напишите запрос, который бы вывел все пары продавцов, живущих в одном и томже городе. Исключите комбинации продавцов с ними же, а также дубликаты строк,выводимых в обратным порядке.2.
Напишите запрос, который вывел бы все пары порядков по данным заказчикам,именам этих заказчиков, и исключал дубликаты из вывода, как в предыдущем вопросе.3. Напишите запрос, который вывел бы имена (cname) и города (city) всех заказчиковс такой же оценкой (rating) как у Hoffmanа. Напишите запрос, использующий полеcnum Hoffmanа а не его оценку, так чтобы оно могло быть использовано если егооценка вдруг изменится.(См. Приложение A для ответов.)10ВСТАВКА ОДНОГОЗАПРОСА ВНУТРЬДРУГОГОВ КОНЕЦ ГЛАВЫ 9, МЫ ГОВОРИЛИ ЧТО ЗАПРОСЫ могут управлять другимизапросами.
В этой главе, вы узнаете как это делается (большей частью), помещая запрос внутрь предиката другого запроса, и используя вывод внутреннего запроса вверном или неверном условии предиката. Вы сможете выяснить какие виды операторов могут использовать подзапросы и посмотреть как подзапросы работают со средствами SQL, такими как DISTINCT, с составными функциями и выводимымивыражения. Вы узнаете как использовать подзапросы с предложением HAVING и получите некоторые наставления как правильно использовать подзапросы.КАК РАБОТАЕТ ПОДЗАПРОС?С помощью SQL вы можете вкладывать запросы внутрь друг друга.
Обычно,внутренний запрос генерирует значение, которое проверяется в предикате внешнегозапроса, определяющего, верно оно или нет.Например, предположим что мы знаем имя продавца: Motika, но не знаем значение его поля snum, и хотим извлечь все порядки из таблицы Порядков. Имеется одинспособ чтобы сделать это (вывод показывается в Рисунке 10.1):SELECT *FROM OrdersWHERE snum = (SELECT snumFROM SalespeopleWHERE sname = 'Motika');Чтобы оценить внешний (основной) запрос, SQL сначала должен оценить внутренний запрос (или подзапрос) внутри предложения WHERE. Он делает это так как идолжен делать запрос имеющий единственную цель — отыскать через таблицу Продавцов все строки, где поле sname равно значению Motika, и затем извлечь значенияполя snum этих строк.Единственной найденной строкой, естественно, будет snum = 1004. Однако SQLне просто выдает это значение, а помещает его в предикат основного запроса вместосамого подзапроса, так чтобы предиката прочитал, чтоWHERE snum = 1004=============== SQL Execution Log ==============| SELECT *|| FROM Orders|| WHERE snum =|| (SELECT snum|| FROM Salespeople|| WHERE sname = 'Motika');||=================================================||onumamtodatecnumsnum || ----------- ---------- --------- ||30021900.10 10/03/199020071004 |=================================================Рисунок 10.1: Использование подзапросаОсновной запрос затем выполняется как обычно с вышеупомянутыми результатами.
Конечно же, подзапрос должен выбрать один и только один столбец, а тип данных этого столбца должен совпадать с тем значением, с которым он будетсравниваться в предикате. Часто, как показано выше, выбранное поле и его значениебудут иметь одинаковые имена (в этом случае, snum), но это необязательно.Конечно, если бы мы уже знали номер продавца Motika, мы могли бы просто напечататьWHERE snum = 1004и выполнять далее с подзапросом в целом, но это было бы не так универсально.
Этобудет продолжать работать даже если номер Motika изменился, а с помощью простого изменения имени в подзапросе, вы можете использовать его для чего угодно.ЗНАЧЕНИЯ, КОТОРЫЕ МОГУТ ВЫДАВАТЬ ПОДЗАПРОСЫСкорее всего было бы удобнее, чтобы наш подзапрос в предыдущем примеревозвращал одно и только одно значение. Имея выбранным поле snum "WHERE city =‘London’" вместо "WHERE sname = ‘Motika’", можно получить несколько различныхзначений. Это может сделать уравнение в предикате основного запроса невозможнымдля оценки верности или неверности, и команда выдаст ошибку.При использовании подзапросов в предикатах основанных на реляционных операторах (уравнениях или неравенствах, как объяснено в Главе 4), вы должны убедиться, что использовали подзапрос который будет выдавать одну и только однустроку вывода.
Если вы используете подзапрос, который не выводит никаких значенийвообще, команда не потерпит неудачи, но основной запрос не выведет никаких значений.Подзапросы, которые не производят никакого вывода (или нулевой вывод), вынуждают рассматривать предикат ни как верный, ни как неверный, а как неизвестный.Однако, неизвестный предикат имеет тот же самый эффект, что и неверный: никакиестроки не выбираются основным запросом (смотри Главу 5 для подробной информации о неизвестном предикате).Это плохая стратегия, чтобы делать что-нибудь подобное следующему:SELECT *FROM OrdersWHERE snum = ( SELECT snumFROM SalespeopleWHERE city = “Barcelona” );Поскольку мы имеем только одного продавца в Barcelona — Rifkin, то подзапросбудет выбирать одиночное значение snum и следовательно будет принят.
Но это —только в данном случае. Большинство SQL баз данных имеют многочисленых пользователей, и если другой пользователь добавит нового продавца из Barcelona в таблицу, подзапрос выберет два значения, и ваша команда потерпит неудачу.DISTINCT С ПОДЗАПРОСАМИВы можете, в некоторых случаях, использовать DISTINCT, чтобы вынудить подзапрос генерировать одиночное значение. Предположим что мы хотим найти все порядки кредитований для тех продавцов которые обслуживают Hoffmanа (cnum = 2001).Имеется один способ, чтобы сделать это (вывод показывается в Рисунке 10.2):SELECT *FROM OrdersWHERE snum = ( SELECT DISTINCT snumFROM OrdersWHERE cnum = 2001 );=============== SQL Execution Log ==============| SELECT *|| FROM Orders|| WHERE snum =|| (SELECT DISTINCT snum|| FROM Orders|| Where cnum = 2001);|| =============================================== ||onumamtodatecnumsnum || ------------- -------------- ------- ||3003767.19 10/03/199020011001 ||30084723.00 10/05/199020061001 ||30119891.88 10/06/199020061001 |=================================================Рисунок 10.2: Использование DISTINCT чтобы вынудить получение одногозначения из подзапросаПодзапрос установил, что значение поля snum совпало с Hoffman — 1001, и затем основной запрос выделил все порядки с этим значением snum из таблицы Порядков (не разбирая, относятся они к Hoffman или нет).
Так как каждый заказчик назначенк одному и только этому продавцу, мы знаем, что каждая строка в таблице Порядков сданным значением cnum должна иметь такое же значение snum. Однако, так как тамможет быть любое число таких строк, подзапрос мог бы вывести много (хотя и идентичных) значений snum для данного поля cnum. Аргумент DISTINCT предотвращаетэто. Если наш подзапрос возвратит более одного значения, это будет указывать наошибку в наших данных — хорошая вещь для знающих об этом.Альтернативный подход должен быть чтобы ссылаться к таблице Заказчиков ане к таблице Порядков в подзапросе. Так как поле cnum — это первичный ключ таблицы Заказчика, запрос выбирающий его должен произвести только одно значение.Это рационально только если вы как пользователь имеете доступ к таблице Порядковно не к таблице Заказчиков. В этом случае, вы можете использовать решение котороемы показали выше.
(SQL имеет механизмы которые определяют — кто имеет привилегии чтобы делать что-то в определенной таблице. Это будет объясняться в Главе22.)Пожалуйста учтите, что методика, используемая в предшествующем примере,применима, только когда вы знаете, что два различных поля в таблице должны всегдасовпадать, как в нашем случае. Эта ситуация не является типичной в реляционныхбазах данных, она являеться исключением из правил.ПРЕДИКАТЫ С ПОДЗАПРОСАМИ ЯВЛЯЮТСЯНЕОБРАТИМЫМИВы должны обратить внимание, что предикаты, включающие подзапросы, используют выражение<скалярная форма> <оператор> <подзапрос>, а не<подзапрос> <оператор> <скалярное выражение> или,<подзапрос> <оператор> <подзапрос>.Другими словами, вы не должны записывать предыдущий пример так:SELECT *FROM OrdersWHERE ( SELECT DISTINCT snumFROM OrdersWHERE cnum = 2001 ) = snum;В строгой ANSI реализации, это приведет к неудаче, хотя некоторые программыи позволяют делать такие вещи.