УСТРАНЕНИЕ ИЗБЫТОЧНОСТИ
Обратите внимание что наш вывод имеет два значение для каждой комбинации,
причем второй раз в обратном порядке. Это потому, что каждое
значение показано первый раз в каждом псевдониме, и второй раз( сим-
метрично) в предикате.
Следовательно, значение 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 не выбирается при наличии того же рейтинга что и
он сам потому что его им не предшествует ему самому в алфавитном
порядке. Если бы вы захотели включить сравнение строк с ними же
=============== SQL Execution Log ==============
| |
| 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: Устранение избыточности вывода в объединении с собой.
в запросах подобно этому, вы могли бы просто использовать
< = вместо <.
ЕЩЕ БОЛЬШЕ КОМПЛЕКСНЫХ ОБЪЕДИНЕНИЙ
Вы можете использовать любое число псевдонимов для одной таблицы
в запросе, хотя использование более двух в данном предложении
SELECT * будет излишеством.
Предположим что вы еще не назначили ваших заказчиков к вашему
продавцу. Компании должна назначить каждому продавцу первоначально трех
заказчиков, по одному для каждого рейтингового значения.
Вы лично можете решить какого заказчика какому продавцу назначить,
но следующий запрос вы используете чтобы увидеть все возможные
комбинации заказчиков которых вы можете назначать.
( Вывод показывается в Рисунке 9.3 ):
SELECT a.cnum, b.cnum, c.cnum
FROM Customers a, Customers b, Customers c
WHERE a.rating = 100
AND b.rating = 200
AND c.rating = 300;
=============== SQL Execution Log ==============
| |
| AND c.rating = 300; |
| =============================================== |
| cnum cnum cnum |
| ----- ------ ------ |
| 2001 2002 2004 |
| 2001 2002 2008 |
| 2001 2003 2004 |
| 2001 2003 2008 |
| 2006 2002 2004 |
| 2006 2002 2008 |
| 2006 2003 2004 |
| 2006 2003 2008 |
| 2007 2002 2004 |
| 2007 2002 2008 |
| 2007 2003 2004 |
| 2007 2003 2008 |
=================================================
Рисунок 9.3 Комбинация пользователей с различными значениями рейтинга
Как вы можете видеть, этот запрос находит все комбинации заказчиков с
трем значениями оценки, поэтому первый столбец состоит из заказчиков
с оценкой 100, второй с 200, и последний с оценкой 300. Они повторяются
во всех возможных комбинациях. Это - сортировка группировки которая
не может быть выполнена с GROUP BY или ORDER BY, поскольку они
сравнивают значения только в одном столбце вывода.
Вы должны также понимать, что не всегда обязательно использовать
каждый псевдоним или таблицу которые упомянуты в предложении FROM
запроса, в предложении SELECT. Иногда, предложение или таблица становятся
запрашиваемыми исключительно потому что они могут вызываться в
предикате запроса. Например, следующий запрос находит всех заказчиков
размещенных в городах где продавец Serres ( snum 1002 ) имеет заказчиков
( вывод показывается в Рисунке 9.4 ):
SELECT b.cnum, b.cname
FROM Customers a, Customers b
WHERE a.snum = 1002
AND b.city = a.city;
=============== SQL Execution Log ============
| |
| SELECT b.cnum, b.cname |
| FROM Customers a, Customers b |
| WHERE a.snum = 1002 |
| AND b.city = a.city; |
| ==============================================|
| cnum cname |
| ------ --------- |
| 2003 Liu |
| 2008 Cisneros |
| 2004 Grass |
=============================================
Рисунок 9.4 Нахождение заказчиков в городах относящихся к Serres.
Псевдоним a будет делать предикат неверным за исключением случая
когда его значение столбца snum = 1002. Таким образом псевдоним
опускает все, кроме заказчиков продавца 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.cname
FROM Customers first, Customers second, Salespeople
WHERE first.snum = second.snum
AND Salespeople.snum = first.snum
AND 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; |
| ====================================================|
| cname snum cname cname |
| ------ ------ -------- -------- |
| Serres 1002 Liu Grass |
| Peel 1001 Hoffman Clemens |
=====================================================
Рисунок 9.5: Объединение таблицы с собой и с другой таблицей