Синтаксис команды для объединения таблицы с собой, тот же что и для объединения многочисленых таблиц, в одном экземпляре. Когда вы объединяете таблицу с собой, все повторяемые имена столбца, заполняются префиксами имени таблицы. Чтобы ссылаться к этим столбцам внутри запроса, вы должны иметь два различных имени для этой таблицы.
Вы можете сделать это с помощью определения временных имен называемых переменными диапазона, переменными корреляции или просто - псевдонимами. Вы определяете их в предложении FROM запроса. Это очень просто: вы набираете имя таблицы, оставляете пробел, и затем набираете псевдоним для нее. Имеется пример который находит все пары заказчиков имеющих один и тот же самый рейтинг (вывод показывается в Таблице 9.1):
SELECT first.cname, second.cname, first.rating
FROM Customers first, Customers second
WHERE first.rating=second.rating;
|
Giovanni |
Giovanni |
200 |
|
Giovanni |
Liu |
200 |
|
Liu |
Giovanni |
200 |
|
Liu |
Liu |
200 |
|
Grass |
Grass |
300 |
|
Grass |
Cisneros |
300 |
|
Clemens |
Hoffman |
100 |
|
Clemens |
Clemens |
100 |
|
Clemens |
Pereira |
100 |
|
Cisneros |
Grass |
300 |
|
Cisneros |
Cisneros |
300 |
|
Pereira |
Hoffman |
100 |
|
Pereira |
Clemens |
100 |
|
Pereira |
Pereira |
100 |
Таблица 9.1: Объединение таблицы с собой
(обратите внимание что в Таблице 9.1, как и в некоторых дальнейших примерах, полный запрос не может уместиться в окне вывода, и следовательно будет усекаться.)
В вышеупомянутой команде, SQL ведет себя так, как если бы он соединял две таблицы называемые 'первая' и 'вторая'. Обе они - фактически, таблицы Заказчика, но псевдонимы разрешают им быть обработаными независимо. Псевдонимы первый и второй были установлены в предложении FROM запроса, сразу после имени копии таблицы. Обратите внимание что псевдонимы могут использоваться в предложении SELECT, даже если они не определены в предложении FROM.
Это - очень хорошо. SQL будет сначала допускать любые такие псевдонимы на веру, но будет отклонять команду если они не определены далее в предложении FROM запроса.
Псевдоним существует - только пока команда выполняется ! Когда запрос заканчивается, псевдонимы используемые в нем больше не имеют никакого значения.
Теперь, когда имеются две копии таблицы Заказчиков, чтобы работать с ними, SQL может обрабатывать эту операцию точно также как и любое другое обьединение - берет каждую строку из одного псевдонима и сравнивает ее с каждой строкой из другого псевдонима.
Обратите внимание что наш вывод имеет два значение для каждой комбинации, причем второй раз в обратном порядке. Это потому, что каждое значение показано первый раз в каждом псевдониме, и второй раз( симметрично) в предикате. Следовательно, значение A в псевдониме сначала выбирается в комбинации со значением B во втором псевдониме, а затем значение A во втором псевдониме выбирается в комбинации со значением B в первом псевдониме. В нашем примере, Hoffman выбрался вместе с Clemens, а затем Clemens выбрался вместе с Hoffman. Тот же самый случай с Cisneros и Grass, Liu и Giovanni, и так далее. Кроме того каждая строка была сравнена сама с собой, чтобы вывести строки такие как - Liu и Liu. Простой способ избежать этого состoит в том, чтобы налагать порядок на два значения, так чтобы один мог быть меньше чем другой или предшествовал ему в алфавитном порядке. Это делает предикат ассиметричным, поэтому те же самые значения в обратном порядке не будут выбраны снова, например:
SELECT tirst.cname, second.cname, first.rating
FROM Customers first, Customers second
WHERE first.rating=second.rating
AND first.cname < second.cname;
Вывод этого запроса показывается в Таблице 9.2.
Hoffman предшествует Periera в алфавитном порядке, поэтому комбинация удовлетворяет обеим условиям предиката и появляется в выводе. Когда та же самая комбинация появляется в обратном порядке - когда Periera в псевдониме первой таблицы сравнтвается с Hoffman во второй таблице псевдонима - второе условие не встречается. Аналогично Hoffman не выбирается при наличии того же рейтинга что и он сам потому что его имя не предшествует ему самому в алфавитном порядке. Если бы вы захотели
SELECT first.cname, second.cname, first.rating FROM Customers first, Customers second
WHERE first.rating=second.rating
AND first.cname < second.cname
|
cname |
cname |
rating |
|
Hoffman |
Pereira |
100 |
|
Giovanni |
Liu |
200 |
|
Clemens |
Hoffman |
100 |
|
Pereira |
Pereira |
100 |
|
Gisneros |
Grass |
300 |
Таблица 9.2: Устранение избыточности вывода в обьединении с собой.
включить сравнение строк с ними же в запросах подобно этому, вы
могли бы просто использовать <=вместо <.
Таким образом мы можем использовать эту особенность SQL для проверки определенных видов ошибок. При просмотре таблицы Порядков, вы можете видеть что поля cnum и snum должны иметь постоянную связь. Так как каждый заказчик должен быть назначен к одному и только одному продавцу, каждый раз когда определенный номер заказчика появляется в таблице Порядков, он должен совпадать с таким же номером продавца. Следующая команда будет определять любые несогласованности в этой области:
SELECT first.onum, tirst.cnum, first.snum,
second.onum, second.cnum,second.snum
FROM Orders first, Orders second
WHERE first.cnum=second.cnum
AND first.snum < > second.snum;
Хотя это выглядит сложно, логика этой команды достаточно проста. Она будет брать первую строку таблицы Порядков, запоминать ее под первым псевдонимом, и проверять ее в комбинации с каждой строкой таблицы Порядков под вторым псевдонимом, одну за другой. Если комбинация строк удовлетворяет предикату, она выбирается для вывода. В этом случае предикат будет рассматривать эту строку, найдет строку где поле cnum=2008 а поле snum=1007, и затем рассмотрит каждую следующую строку с тем же самым значением поля cnum. Если он находит что какая -то из их имеет значение отличное от значения поля snum, предикат будет верен, и выведет выбранные поля из текущей комбинации строк. Если же значение snum с данным значением cnum в наш таблице совпадает, эта команда не произведет никакого вывода.