|
|
Соединения: Выбор данных из нескольких таблиц
В этой главе начинается обсуждение операций, которые связаны с выбором данных из нескольких таблиц. Эти таблицы могут быть расположены как в одной и той же базе данных (локальные таблицы), так и в разных базах данных. До сих пор рассматривались примеры выбора данных из одной таблицы. В этой главе рассматривается мультитабличная операция соединения (join). Подзапросы, которые обращаются к нескольким таблицам, будут рассмотрены в главе 5 “Подзапросы: Использование запросов внутри других запросов”. Часто cоединения могут выступать в качестве подзапросов. В этой главе обсуждаются следующие темы: · Общий обзор операций соединения; · Как соединять таблицы в запросе; · Как SQL Сервер выполняет соединение; · Как влияют неопределенные значения на соединение; · Как указывать столбцы для соединения. Соединение двух и более таблиц можно рассматривать как процесс сравнения данных в указанных столбцах этих таблиц и формирования новой таблицы из строк исходных таблиц, которые дают положительный результат при сравнении. Оператор join (соединить) сравнивает данные в указанных столбцах каждой таблицы строка за строкой и компонует из строк, прошедших сравнение, новые строки. Обычно в качестве операции сравнения выступает равенство, т.е. данные сравниваются на полное совпадение, но возможны и другие типы соединения. Результаты соединения будут иметь содержательный смысл, если сравниваемые величины имеют один и тот же тип или подобные типы. Операция соединения имеет свой собственный жаргон. Слово “join” может использоваться и как глагол и как существительное, кроме того оно может означать либо операцию, либо запрос, содержащий эту операцию, либо результаты этого запроса. Имеется также несколько разновидностей соединений: соединения с равенством (эквисоединения), естественные (natural) соединения, внешние соединения и т.д. Наиболее часто встречающейся разновидностью соединений являются соединения, основанные на равенстве. Ниже приведен пример запроса на соединение, в котором ищутся имена авторов и издателей, живущих в одном и том же городе: select au_fname,
au_lname, pub_name from authors,
publishers where
authors.city = publishers.city au_fname
au_lname pub_name -------------
------------
----------------------------- Cheryl
Carson Algodata
Infosystems Abraham
Bennet Algodata
Infosystems (Выбрано 2
строки) Поскольку требуемая информация находится в двух таблицах publishers и authors, то для ее выбора необходимо соединение этих таблиц. Операция соединения является отличительным признаком реляционной модели данных в системах управления базами данных (СУБД). Причем это самый существенный признак реляционных систем управления базами данных, который отличает их от систем других типов. В структурных СУБД, известных также как сетевые или иерархические системы, связи между данными должны быть заранее определены. В таких системах после создания базы данных уже трудно сделать запрос относительно связей между данными, которые не были заранее предусмотрены. В реляционных СУБД, наоборот, при создании базы данных связи между данными не фиксируются. Они проявляются лишь при обработке данных, т.е. в момент запроса к базе данных, а не при ее создании. Можно обратиться с любым запросом, который приходит в голову, относительно хранящейся в базе информации, независимо от того с какой целью создавалась эта база. В соответствии с правилами проектирования баз данных, известными как правила нормализации, каждая таблица должна описывать один вид сущностей - человека, место, событие или вещь. По этой причине, когда нужно сравнить информацию, относящуюся к различным объектам, необходима операция соединения. Взаимосвязи, существующие между данными, расположенными в различных таблицах, проявляются путем их соединения. Как следствие из этого правила, операция соединения дает неограниченную гибкость в добавлении новых видов данных в базу. Всегда можно создать новую таблицу, которая содержит данные, относящиеся к разным сущностям. Если новая таблица имеет поле, подобное некоторому полю в уже существующей таблице, то его можно добавить в эту таблицу путем соединения. Оператор соединения, как и оператор выбора, начинается с ключевого слова select. Данные из столбцов, указанных после этого ключевого слова, включаются в результаты запроса в нужном порядке. В предыдущем примере это были столбцы с именами и фамилиями писателей и названиями издательств. Названия столбцов в этом примере pub_name, au_lname и au_fname не нужно уточнять названием таблицы, поскольку здесь нет неоднозначности относительно того, какой таблице они принадлежат. Но название столбца city, который используется в операции сравнения уже нуждается в уточнении, поскольку столбцы с таким названием имеются в обеих таблицах. Хотя в этом примере ни один из столбцов city не появляется в результатах запроса, SQL Серверу необходимо уточнение для выполнения операции сравнения. Как и в операторе выбора, здесь можно включить все столбцы в результат запроса с помощью сокращения “*”. Например, для того чтобы включить все столбцы таблиц authors и publishers в результат предыдущего соединения, необходимо выполнить следующий запрос: select * from authors,
publishers where
authors.city = publishers.city au_id au_lname au_fname phone address city state postalcode contract pub_id pub_name city state --------------- ---------- ---------- ------------------ ---------------- ------ -------------- -------- --------- ----------------------------- ---------
238-95-7766 Carson Cheryl 415 548-7723 589
Darwin Ln. Berkeley CA 94705 1 1389 Algodata Infosystems Berkeley
CA 409-56-7008 Bennet Abraham 415 658-9932 223
Bateman St Berkeley CA 94705 1 1389 Algodata Infosystems Berkeley
CA (Выбрано 2
строки) Отсюда видно, что результирующая строка составлена из строк исходных таблиц и состоит из тринадцати столбцов каждая. Поскольку ширины печатной страницы не хватает, то каждая результирующая строка размещается на двух текстовых строках. Когда используется символ “*”, то столбцы выводятся в том порядке, в каком они расположены в таблицах. В списке выбора можно указать названия столбцов только из одной таблицы, участвующей в соединении. Например, чтобы найти авторов, живущих в одном городе с некоторым издателем, не обязательно указывать названия столбцов из таблицы publishers: select au_lname, au_fname from authors,
publishers where
authors.city = publishers.city Необходимо помнить, что, как и в любом операторе выбора, названия столбцов в списке выбора и названия таблиц в предложении (конструкции) from должны разделяться запятыми. В предложении from оператора соединения указываются названия всех таблиц и вьюверов, участвующих в соединении. Именно это предложение указывает SQL Серверу, что необходимо выполнить соединение. Таблицы и вьюверы в этом предложении можно указывать в произвольном порядке. Порядок расположения названий таблиц влияет на результат только при использовании сокращения “*” в списке выбора. В предложении from можно указывать от 2 до 16 отдельных названий для таблиц или выюверов. При подсчете максимально допустимого числа нужно учитывать, что отдельным членом этого предложения считаются следующие названия: · Название таблицы (или вьювера), указанное в предложении from; · Каждая копия названия одной и той же таблицы (самосоединение); · Название таблицы, указанное в подзапросе; · Названия базовых таблиц, на которые ссылаются вьюверы, указанные в предложении from. Соединения, в которых участвует более двух таблиц, рассматриваются далее в главе “Соединение более двух таблиц”. Как отмечалось во второй главе “Запросы: Выбор данных из таблицы”, названия таблиц и вьюверов могут уточняться названием владельца и названием базы данных. Вьюверы можно использовать точно также, как и таблицы. В главе 9 будут рассмотрены вьюверы, но во всех приводимых там примерах будут использоваться только таблицы. В предложении where
(где) указываются отношения, которые устанавливаются между таблицами,
перечисленными в предложении from,
для выбора результирующих строк. В нем приводятся названия столбцов, по которым
производится соединение, дополненные при необходимости названиями таблиц, и
операция сравнения, которой обычно является равенство, но иногда здесь могут
встречаться и отношения “больше чем” или “меньше чем”. Детальное описание
синтаксиса предложения where приводится
в главе 2 этого руководства и в главе “Предложение where” в Справочном руководстве SQL Сервера. Замечание. Можно получить совершенно неожиданный результат, если опустить предложение where в операторе соединения. Без этого предложения все вышеприведенные запросы на соединение будут выдавать 27 строк вместо 2. В следующем разделе будет объяснено почему так происходит. Соединения, в которых данные сравниваются на совпадение, называются эквисоединениями (equijoins). Более точное определение эквисоединения дается позже в этой главе, также как и примеры соединений, основанных не на равенстве. Соединение может основываться на следующих операциях сравнения: Таблица 4.1. Операции сравнения
Соединения, основанные на операциях сравнения, в общем называются тетасоединениями (theta joins). Другой класс соединений образуют внешние соединения, которые рассматриваются позже в этой же главе. К числу внешних операций соединения относятся следующие операции. Таблица 4.2. Операции внешнего соединения
Названия соединяемых столбцов могут не совпадать, хотя на практике они часто совпадают. Кроме того, они могут содержать данные различных типов (см. главу 7). Однако, если типы данных не совпадают, то они должны быть совместимыми, чтобы SQL Сервер мог
автоматически преобразовать их между собой. Например, SQL Сервер автоматически
преобразует друг в друга любые числовые типы данных: int, smallint, tinyint, decimal, float, а также любые строковые
типы и типы даты: char, varchar, nchar, nvarchar и datetime. Более детально преобразование типов рассматривается в главе
10 “Использование встроенных функций в запросах” и в главе “Функции
преобразования типов данных” Справочного
руководства SQL Сервера.
Замечание. Таблицы нельзя соединять по текстовым или графическим полям. Однако можно сравнивать длины текстовых полей в предложении where, например, следующим образом: where datalength(textab_1.textcol) > datalength(textab_2.textcol) Предложение where оператора соединения может включать и другие условия, отличные от условия соединения. Другими словами, операторы соединения и выбора можно объединить в одном SQL операторе. Далее в этой главе будут приведены соответствующие примеры. Знание того, как выполняется соединения помогает в их понимании и позволяет объяснить, почему получаются неожиданные результаты, когда соединение задано неправильно. В этом разделе описывается процесс выполнения соединения в концептуальном плане. Конечно, SQL Сервер выполняет эту процедуру более сложным образом. Вообще говоря, первый шаг в выполнении соединения состоит в образовании декартова произведения таблиц, т.е. в образовании всех возможных комбинаций строк этих таблиц друг с другом. Число строк в декартовом (прямом) произведении двух таблиц, равно произведению числа строк в первой таблице на число строк во второй таблице. Например, число строк в декартовом произведении таблиц author и publishers равно 69 ( 23 автора, умноженные на 3 издателя). Декартово произведение строится в любом запросе, который содержит более одной таблицы в списке выбора, более одной таблицы в предложении from и не содержит предложения where. Например, если убрать предложение where из предыдущего запроса на соединение, то SQL Сервер скомбинирует 23 автора с 3 издателями и возвратит в результате 69 строк. Декартово произведение не содержит какой-либо полезной информации. На самом деле оно даже вводит в заблуждение, поскольку создает видимость, что каждый автор имеет отношение к каждому издателю, что совершенно неверно. По этой причине соединение должно включать предложение where, которое отбирает связанные между собой строки и указывает как именно они должны быть связаны. Оно может включать также дополнительные ограничения. Из декартового произведения происходит удаление тех строк, которые не удовлетворяют условиям в предложении where. В предыдущем примере предложение where удаляет те строки, в которых город, где проживает автор, отличен от города, где живет издатель. Еквисоединением называется соединение, в котором данные в столбцах сравниваются на равенство, и все столбцы соединяемых таблиц включаются в результат. Запрос, который был рассмотрен ранее: select * from authors,
publishers where
authors.city = publishers.city является примером еквисоединения. В результате этого запроса столбец city появляется дважды. Из определения следует, что результат эквисоединения содержит два одинаковых столбца. Поскольку обычно нет необходимости повторять одну и ту же информацию, то один из этих столбцов можно удалить путем модификации запроса. Результат этой модификации, показанный далее, называется естественным соединением. select
publishers.pub_id, publishers.pub_name, publishers.state, authors.* from
publishers, authors where
publishers.city = authors.city В этом примере столбец publishers.city уже не появится в результате запроса. Предложение where запроса на соединение может содержать кроме условия соединения, также дополнительные критерии отбора. Например, для выбора названий и издателей всех книг, по которым был выплачен аванс больший чем 7500 долларов, можно воспользоваться следующим запросом: select title, pub_name, advance from titles, publishers where titles.pub_id = publishers.pub_id and advance > $7500 title pub_name advance ---------------------------------------------- ---------------------------- ------------- You Can Combat Computer
Stress! New Age Books 10,125.00 The Gourmet Microwave Binnet
& Hardley 15,000.00 Secrets of Silicon Valley Algodata Infosystems 8,000.00
Sushi, Anyone? Binnet
& Hardley
8,000.00 (Выбрано 4 строки) Заметим, что столбцы, по которым происходит соединение, не обязательно должны включаться в список выбора, поэтому в данном случае их нет в результате. В оператор соединения можно включать произвольное число дополнительных критериев отбора. Порядок следования этих критериев и условия соединения не имеет значения. Условие соединения таблиц не обязательно является равенством. Здесь можно использовать любую другую операцию сравнения: не равно (!=), больше чем (>), меньше чем (<), больше или равно (>=), меньше или равно (<=). Язык Transact-SQL также содержит операции !> и !< , которые эквивалентны операциям меньше или равно и больше или равно соответственно. В следующем примере используется операция “больше чем” для нахождения авторов, которые публиковались издательством New Age Books и которые живут в штатах, названия которых больше чем название штата Массачусетс (в алфавитном порядке): select pub_name,
publishers.state, au_lname, au_fname, authors.state from
publishers, authors where
authors.state > publishers.state and pub_name = "New Age Books" pub_name state
au_lname au_fname state --------------------- ------ ----------------- ---------------- -----
New Age Books MA Greene Morningstar TN New Age Books MA Blotchet-Halls Reginald OR New Age Books MA del Castillo Innes MI New Age Books MA Panteley Sylvia MD New Age Books MA Ringer Anne UT
New Age Books MA Ringer Albert UT (Выбрано 6
строк) В следующем примере в соединении используются операции >= и < для правильного нахождения скидок (royalty) в таблице roysched, связанных с общим объемом продаж: select
t.title_id, t.total_sales, r.royalty from titles t,
roysched r where
t.title_id = r.title_id and t.total_sales >= r.lorange and t.total_sales
< r.hirange title_id total_sales royalty ----------- ----------- ------- ---------- BU1032
4095 10 BU1111
3876 10 BU2075
1872 24 BU7832
4095 10 MC2222
2032 12 MC3021 22246 24 PC1035
8780 16 PC8888
4095 10 PS1372 375 10 PS2091
2045 12 PS2106
111 10 PS3333
4072 10 PS7777
3336 10 TC3218 375 10 TC4203 15096 14 TC7777 4095 10 (Выбрано 16
строк) Можно соединять между собой столбцы одной и той же таблицы с помощью самосоединения (self-join). Например, можно использовать самосоединение для нахождения авторов, живущих в городе Окленде штата Калифорния в одном и том же почтовом округе. Поскольку этот запрос включает столбцы одной таблицы authors, то эта таблица выступает в двух ролях. Чтобы различить эти роли, необходимо временно присвоить ей в предложении from различные коррелирующиеся (согласующиеся) названия, такие как au1 и au2. Эти согласующиеся названия будут использоваться для уточнения названий столбцов в следующем запросе. В этом случае самосоединение выглядит следующим образом: select
au1.au_fname, au1.au_lname, au2.au_fname, au2.au_lname from authors
au1, authors au2 where au1.city
= "Oakland" and au2.city = "Oakland" and au1.state
= "CA" and au2.state = "CA" and
au1.postalcode = au2.postalcode au_fname
au_lname au_fname au_lname ------------
----------- ----------- ------------ Marjorie
Green Marjorie Green Dick
Straight Dick Straight Dick
Straight Dirk Stringer Dick
Straight Livia Karsen Dirk
Stringer Dick Straight Dirk
Stringer Dirk Stringer Dirk
Stringer Livia Karsen Stearns
MacFeather Stearns MacFeather Livia
Karsen Dick Straight Livia Karsen Dirk
Stringer Livia
Karsen Livia Karsen (Выбрано 11
строк) Чтобы исключить из результатов этого запроса строки, в которых авторы соединяются сами с собой, а также строки, отличающиеся лишь порядком следования авторов, необходимо добавить в самосоединение дополнительное условие: select
au1.au_fname, au1.au_lname, au2.au_fname, au2.au_lname from authors
au1, authors au2 where au1.city
= "Oakland" and au2.city = "Oakland" and au1.state
= "CA" and au2.state = "CA" and
au1.postalcode = au2.postalcode and au1.au_id
< au2.au_id au_fname
au_lname au_fname au_lname ---------
----------- ----------- ---------- Dick Straight Dirk Stringer Dick Straight Livia Karsen Dirk
Stringer Livia Karsen (Выбрано 3 строки) Теперь понятно, что Дик Страйт, Дик Стрингер и Ливия Карсен живут в одном и том же почтовом округе.
Условие “не равно” особенно полезно для отбора строк при самосоединении. Например, это условие используется в следующем самосоединении для нахождения всех категорий (типов) книг, в которых есть по крайней мере две недорогих (меньше чем 15 долларов) книги с различными ценами: select
distinct t1.type, t1.price from titles
t1, titles t2 where t1.price
<$15 and t2.price <$15 and t1.type =
t2.type and t1.price
!= t2.price type price ----------
-------- business 2.99 business 11.95 psychology 7.00 psychology 7.99 psychology 10.95 trad_cook 11.95 trad_cook 14.99 (Выбрано 7 строк) Замечание. Выражение “not название_столбца1 = название_столбца2” эквивалентно выражению “название_столбца1 != название_столбца2”. В следующем примере соединение с условием “не равно” комбинируется с самосоединением. В этом запросе ищутся строки в таблице titleauthor, у которых одинаково значение поля title_id, но различно значение поля au_id, т.е. ищутся книги, у которых, по крайней мере, два автора. select
distinct t1.au_id, t1.title_id from
titleauthor t1, titleauthor t2 where
t1.title_id = t2.title_id and t1.au_id != t2.au_id order by
t1.title_id au_id
title_id ----------------
----------- 213-46-8915
BU1032 409-56-7008
BU1032 267-41-2394
BU1111 724-80-9391 BU1111
722-51-5454
MC3021 899-46-2035
MC3021 427-17-2319
PC8888 846-92-7186
PC8888 724-80-9391
PS1372 756-30-7391
PS1372 899-46-2035
PS2091 998-72-3567
PS2091 267-41-2394 TC7777 472-27-2349 TC7777 672-71-3249 TC7777 (Выбрано 15
строк) Иногда соединения с условием “не равно” бывает недостаточно и его необходимо заменить подзапросом. Например, предположим, что необходимо получить список авторов, которые живут в городах, где нет издательств. Для простоты ограничим этот список авторами, фамилии которых начинаются на буквы “А”, “В” и “С”. Запрос с условием “не равно” может иметь следующий вид: select
distinct au_lname, authors.city from
publishers, authors where au_lname
like "[ABC]%" and publishers.city != authors.city Но получаемые на него результаты вовсе не являются ответом на этот вопрос! au_lname
city ---------------- ------------ Bennet
Berkeley Carson
Berkeley Blotchet-Halls Corvallis Система интерпретирует этот SQL оператор следующим образом: “Найти фамилии авторов, которые живут в городе, в котором нет некоторого издательства”. Все авторы, имеющиеся в таблице, удовлетворяют этому условию, включая авторов, живущих в Беркли, в котором расположено издательство Algodata Inforsystems. В этом случае способ, которым система выполняет соединение (предварительно строя все возможные комбинации с последующей проверкой остальных условий), является причиной появления нежелательного результата. В случаях подобных этому необходимо использовать подзапрос для получения желаемого результата. Подзапрос может выполнить предварительное удаление ненужных строк, а затем уже будет выполняться последующий отбор. Правильный оператор будет иметь следующий вид: select
distinct au_lname, city from authors where au_lname
like "[ABC]%" and city not in (select city
from publishers where
authors.city = publishers.city) Теперь получается нужный результат: au_lname
city ------------------- ------------ Blotchet-Halls
Corvallis (Выбрана 1 строка) Подзапросы будут подробно рассмотрены в главе 6. Таблица titleauthor базы pubs2 дает хороший пример ситуации, в которой полезно соединить более чем две таблицы. Чтобы найти названия всех книг заданного типа и имена их авторов, можно использовать следующий запрос: select au_lname,
au_fname, title from authors,
titles, titleauthor where
authors.au_id = titleauthor.au_id and
titles.title_id = titleauthor.title_id and
titles.type = "trad_cook" au_lname
au_fname title --------------
-----------
----------------------------------------------- Panteley Sylvia Onions, Leeks, and Garlic: Сooking Secrets of the Mediterranean Blotchet-Halls
Reginald Fifty Years in
Buckingham Palace Kitchens O'Leary
Michael Sushi, Anyone? Gringlesby
Burt Sushi, Anyone? Yokomoto Akiko Sushi, Anyone? (Выбрано 5 строк) Заметим, что одна из таблиц в предложении from, а именно titleauthor, не передает в окончательный результат свои данные, поскольку ни данные из столбца au_id, ни данные из столбца title_id не включены в результат. Однако само соединение стало возможным лишь при использовании этой таблицы как промежуточной. В одном операторе можно также соединять по более чем двум столбцам. Например, в следующем запросе показан общий объем продаж каждой книги, интервал в который попадает этот объем, и результирующая скидка: select
titles.title_id, total_sales, lorange, hirange, royalty from titles,
roysched where
titles.title_id = roysched.title_id and total_sales >= lorange and total_sales
< hirange title_id total_sales lorange hirange
royalty ----------------------- ---------- ------- ---- BU1032 4095 0
5000 10 BU1111 3876 0
4000 10 BU2075 18722 14001 50000 24 BU7832 4095 0
5000 10 MC2222 2032
2001 4000 12 MC3021 2224 12001 50000 24 PC1035 8780 4001 10000 16 PC8888 4095 0
5000 10 PS1372
375 0
10000 10 PS2091 2045 1001 5000 12 PS2106
111 0
2000 10 PS3333 4072 0
5000 10 PS7777 3336 0
5000 10 TC3218
375 0
2000 10 TC4203 15096
8001 6000 14 TC7777 4095 0
5000 10 (Выбрано 16 строк) Когда в одном операторе совмещаются несколько соединений или когда соединяются более двух таблиц, “соединительные выражения” обычно связываются логической операцией and (И), как это было в предыдущих примерах. Однако, их можно связывать и логической операции or (ИЛИ). В рассматриваемых ранее соединениях в результат включались только строки, которые удовлетворяли условию соединения. По существу эти соединения исключали информацию, которая содержалась в строках, которые не удовлетворяли этому условию. Однако, иногда, в результат желательно включить именно информацию, которая содержится в этих строках. В таких случаях нужно использовать внешнее соединение. Язык Transact-SQL является одной из версий языка SQL, которая содержит внешние соединения. Операции внешнего соединения в языке Transact-SQL имеют следующий вид: Таблица 4.3. Список операций внешнего соединения
Напомним, что запрос, в котором искались авторы, проживающие в одном городе с издателем, возращал двух людей: Абрахама Беннета и Черил Карсон. Чтобы включить в результат всех авторов независимо от местонахождения издателя, необходимо использовать внешнее соединение. Соответствующий запрос и его результаты имеют следующий вид: select au_fname,
au_lname, pub_name from authors,
publishers where
authors.city *= publishers.city au_fname
au_lname pub_name -----------
--------------
---------------------------- Johnson
White NULL Marjorie
Green NULL Cheryl
Carson Algodata
Infosystems Michael
O'Leary NULL Dick
Straight NULL Meander
Smith NULL Abraham
Bennet Algodata
Infosystems Ann
Dull NULL Burt
Gringlesby NULL Chastity
Locksley NULL Morningstar
Greene NULL Reginald
Blotche-Halls NULL Akiko
Yokomoto NULL Innes
del Castillo NULL Michel
DeFrance NULL Dirk
Stringer NULL Stearns
MacFeather NULL Livia Karsen NULL Sylvia
Panteley NULL Sheryl
Hunter NULL Heather
McBadden NULL Anne
Ringer NULL Albert
Ringer NULL (Выбраны 23 строки) Операция сравнения “*=” отличает внешнее соединение от обычного. Это “левое” внешнее соединение, которое сообщает SQL Серверу, что необходимо включить в результат все строки первой таблицы authors, независимо от результата сравнения их с полем city таблицы publishers. Заметим, что для большинства авторов результат сравнения отрицательный, поэтому в столбец pub_name в этом случае записывается неопределенное значение NULL. Заметим также, что правая таблица publishers называется в этом случае внутренней таблицей внешнего соединения. Замечание. Поскольку столбцы с данными типа “бит” не допускают неопределенных значений, то при их внешнем соединении в соответствующих позициях записывается “0”. “Правое” внешнее соединение задается операцией сравнения “=*”, которая указывает, что в результат должны включаться все строки из второй таблицы независимо от выполнения условия сравнения с соответствующим полем первой таблицы. В этом случае первая таблица называется внутренней. Если ввести эту операцию в предыдущий запрос, то получим следующий результат: select au_fname,
au_lname, pub_name from authors,
publishers where
authors.city =* publishers.city au_fname
au_lname pub_name ---------
---------
-------------------- NULL NULL New Age Books NULL
NULL Binnet & Hardley Cheryl
Carson Algodata Infosystems Abraham
Bennet Algodata Infosystems (Выбраны 4
строки) Можно и дальше уточнять результаты внешнего соединения путем сравнения их с константой. Это означает, что точно будут указываться только те величины, которые действительно необходимы, а остальные как бы оказываются за чертой. Для примера рассмотрим сначала эквисоединение, а затем сравним его с внешним соединением. Предположим, что необходимо найти все книги, объем продаж которых в некотором магазине оказался больше 500 экземпляров: select
distinct salesdetail.stor_id, title from titles,
salesdetail where qty >
500 and
salesdetail.title_id = titles.title_id stor_id
title
-------
------------------------------------------------------------------------ 5023
Sushi, Anyone? 5023 Is
Anger the Enemy? 5023 The
Gourmet Microwave 5023 But
Is It User Friendly? 5023
Secrets of Silicon Valley 5023
Straight Talk About Computers 5023 You
Can Combat Computer Stress! 5023
Silicon Valley Gastronomic Treats
5023
Emotional Security: A New Algorithm 5023 The
Busy Executive's Database Guide 5023
Fifty Years in Buckingham Palace Kitchens 5023
Prolonged Data Deprivation: Four Case Studies 5023
Cooking with Computers: Surreptitious Balance Sheets 7067
Fifty Years in Buckingham Palace Kitchens (Выбрано 14
строк) Чтобы увидеть кроме того книги, объем продаж которых ни в одном магазине не был больше 500 экземпляров, можно использовать внешнее соединение: select
distinct salesdetail.stor_id, title from titles,
salesdetail where qty >
500 and salesdetail.title_id
=* titles.title_id stor_id title
------- ----------
------------------------------------------------------------ NULL Net Etiquette NULL Life Without Fear 5023 Sushi, Anyone? 5023 Is Anger the Enemy? 5023 The Gourmet Microwave 5023 But Is It User Friendly? 5023 Secrets of Silicon Valley 5023 Straight Talk About Computers 5023 You Can Combat Computer Stress! 5023 Silicon Valley Gastronomic Treats 5023 Emotional Security: A New
Algorithm 5023 The Busy Executive's Database
Guide 5023 Fifty Years in Buckingham Palace
Kitchens 7067 Fifty Years in Buckingham Palace Kitchens 5023 Prolonged Data Deprivation: Four Case
Studies 5023 Cooking with Computers: Surreptitious
Balance Sheets NULL Computer Phobic and Non-Phobic
Individuals: Behavior Variations NULL Onions, Leeks, and Garlic: Cooking Secrets of the Mediterranean (Выбрано 18
строк) В языке Transact-SQL нельзя одну таблицу использовать и во внешнем соединении и в обычном соединении. Следующий запрос является ошибочным, поскольку таблица salesdetail участвует одновременно в двух соединениях: select
distinct sales.stor_id, stor_name, title from sales,
stores, titles, salesdetail where qty >
500 and
salesdetail.title_id =* titles.title_id and
sales.stor_id = salesdetail.stor_id and
sales.stor_id = stores.stor_id Msg 303, Level 16, State 1: Server 'RAW', Line 1: The table 'salesdetail' is an inner member of an
outer-join clause. This is not allowed if the table also participates in a
regular join clause. (Таблица ‘salesdetail’ является внутренним членом внешнего соединения. Это недопустимо, поскольку эта таблица также участвует в обычном соединении.) Если необходимо определить название магазина, продавшего более 500 экземпляров некоторой книги, нужно сделать второй запрос. Если запрос с внешним соединением содержит также условие отбора по столбцу внутренней таблицы, то могут получиться неожиданные результаты. В этом случае условие отбора не повлияет на число выводимых строк, но приведет к появлению неопределенных значений в столбцах внутренней таблицы в тех строках, которые не удовлетворяют этому условию. Если в столбцах соединяемых таблиц имеются неопределенные значения, то они будут всегда давать отрицательный результат при сравнении. В частности, отрицательный результат будет получаться и при сравнении значения NULL с NULL. Поскольку значение NULL представляет собой неизвестное или невозможное значение, то нет никаких оснований надеяться, что две неизвестные величины совпадают друг с другом. Присутствие неопределенных значений в соединяемых таблицах можно обнаружить только при внешнем соединении. Здесь для примера приведены две таблицы, каждая из которых содержит неопределенные значения в столбцах, которые участвуют в соединении. При левом внешнем соединении можно увидеть неопределенные значения в первой таблице. Таблица
1: a b ------------
-------- 1 one NULL three 4 join4 Таблица
2: c
d ---------
------- NULL two
4 four Левое внешнее соединение: select * from t1, t2 where a *= c a b c
d ------- ------- -------- --------
1 one NULL NULL NULL
three NULL NULL 4 join4
4 four Заметим, что в этом результате непросто различить неопределенные значения, имеющиеся в таблице, от неопределенных значений, появившихся в результате соединения. Поэтому, когда в таблице имеются неопределенные значения, лучше удалить их из результатов путем использования обычного соединения. Имеется системная процедура SP_HELPJOINS, которая указывает наиболее подходящие для соединения столбцы двух таблиц. Ее можно вызвать с помощью следующей команды: sp_helpjoins таблица1, таблица2 Например, можно вызвать эту процедуру с таблицами titleauthor и titles в качестве аргументов: sp_helpjoins
titleauthor, titles Процедура SP_HELPJOINS выбирает пары столбцов для соединения на основе двух условий. Во-первых, просматривается таблица syskeys (системные ключи) текущей базы даных для поиска ключей импорта (foreign keys) процедурой SP_FOREIGNKEY. Затем проверяется есть ли у этих таблиц общие ключи с помощью процедуры SP_COMMONKEY. Если общих ключей нет, то ищутся любые ключи, подходящие для соединения. Наконец, если таких ключей найти не удалось, то выбираются столбцы с одинаковым названием или одинаковым типом данных. Более полная информация о системных процедурах дается в Справочном руководстве по SQL Серверу.
|
Дизайн: Piton Alien |