В запросе можно объединять данные из одной или нескольких таблиц. Такое объединение таблиц называется соединением (связыванием) таблиц. Различают внутреннее и внешнее соединения.
Внутреннее соединение – это соединение, при котором результирующий набор получается путем перечисления нужных полей из разных таблиц после слова SELECT. При этом таблицы должны находиться в отношении «один-к-одному».
Например,
SELECT R.fam, R.birthday,A.Foto
FROM Rabotniki R, Advanced A
где таблицы Rabotniki и Advanced содержат основные и дополнительные сведения о работниках предприятия. Связь «один-к-одному». Таблице Rabotniki дан псевдоним R, а таблице Advanced дан псевдоним A.
p
В предыдущем примере перед именем поля записано имя таблицы. Имена поля и таблицы отделяются точкой. Имя таблицы указывать необходимо, если имена полей повторяются для различных таблиц.
Если применить внутреннее соединение к таблицам, связанным по принципу «один-ко-многим», то результирующий набор может содержать избыточную информацию. Для устранения избыточности используют критерии отбора.
Пример 1. Дана БД Sotrudniki, состоящая из двух таблиц:
Запрос внутреннего соединения таблиц, связанных отношением «один-ко-многим» имеет вид
SELECT S_Fio,S_Birthday,D_Nazv FROM Sotrudniki, Doljn
Число записей в результирующем наборе данных равно произведению числа записей в таблице Sotrudniki на число записей в таблице Doljn. Результирующий набор данных имеет вид и содержит избыточную информацию:
Для ограничения числа записей в результирующем наборе данных применяют критерии отбора. Запрос в этом случае будет иметь вид
SELECT S_Fio,S_Birthday,D_Nazv FROM Sotrudniki, Doljn
WHERE S_Doljn=D_Code
Число записей в результирующем наборе данных будет равно числу записей в таблице Sotrudniki. p
Пример 2. Требуется для БД Sotrudniki сформировать запрос, который позволит получить список сотрудников, имеющих должность «программист». Получим
SELECT S_fio, S_birthday FROM sotrudniki, doljn
WHERE S_doljn=D_code and D_nazv='программист'
p
В SQL-запросах допускается самообъединение таблицы. В этом случае одной таблице даются два псевдонима.
Например, для нахождения всех ровесников в таблице Sotrudniki можно написать запрос:
SELECT s1.s_fio, s2.s_fio, s1.s_birthday
FROM Sotrudniki s1, Sotrudniki s2
WHERE (EXTRACT(YEAR
FROM s1.s_birthday)=EXTRACT(YEAR
FROM s2.s_birthday))
AND (s1.s_fio!=s2.s_fio) AND (s1.s_fio<s2.s_fio)
Последнее условие упорядочивает фамилии и исключает дублирование результатов.
p
При внутреннем соединении все таблицы, поля которых указаны в SQL-запросе, являются равноправными. То есть каждой записи в первой таблице находилась соответствующая ей запись во второй таблице.
При внешнем объединении (outer join) в результирующий набор включаются записи независимо от того, есть ли соответствующее поле во второй таблице. Существует три типа внешнего объединения.
1) LEFT OUTER JOIN … ON – левое, включает в результат все записи первой таблицы, даже те, для которых не имеется соответствия во второй.
2) RIGHT OUTER JOIN … ON – правое, включает в результат все записи второй таблицы, даже те, для которых не имеется соответствия в первой.
3) FULL OUTER JOIN … ON – полное, включает в результат объединение записей обеих таблиц, независимо от их соответствия.
При внешнем соединении можно говорить о том, какая из таблиц является главной. В первом случае – левая, во втором – правая.
Например. Пусть в таблице Sotrudniki БД Sotrudniki есть фамилии, имеющие должность, не указанную в таблице Doljn, и есть должности в таблице Doljn, для которых нет фамилии в таблице Sotrudniki. Тогда
1) SELECT * FROM Sotrudniki LEFT OUTER JOIN Doljn
ON S_doljn=D_code
- результат включат все поля и таблицы Sotrudniki и таблицы Doljn. Число строк соответствует числу записей таблицы Sotrudniki. В строках, относящихся к записям, для которых в Doljn не нашлось соответствие, поля таблицы Doljn остаются пустыми.
2) SELECT * FROM Sotrudniki RIGHT OUTER JOIN Doljn
ON S_doljn=D_code
- число строк соответствует числу записей таблицы Doljn. В строках, относящихся к записям, для которых в Sotrudniki не нашлось соответствие, поля таблицы Sotrudniki остаются пустыми.
3) SELECT * FROM Sotrudniki FULL OUTER JOIN Doljn ON
S_doljn=D_code
- к строкам, относящимся к таблице Sotrudniki добавлены строки, относящиеся к таблице Doljn, для которых нет соответствия в таблице Sotrudniki.
p
В SQL-запросе можно использовать запросы, вложенные в первый. Это можно применить и к операторам, возвращающих совокупные характеристики, и к операторам, возвращающим множество значений.
Например,
1) Определить всех однофамильцев в таблицах Sotrudniki и Sotrudniki1, имеющих одинаковую структуру:
SELECT * FROM Sotrudniki
WHERE S_fio IN (SELECT S_fio FROM Sotrudniki1)
- вложенный оператор SELECT возвращает множество фамилий из таблицы Sotrudniki1, а конструкция WHERE основного оператора SELECT отбирает в таблице Sotrudniki те записи, которые имеются во множестве фамилий из таблицы Sotrudniki1.
2) Вывести из БД Sotrudniki фамилию (фамилии) самого молодого сотрудника:
SELECT S_Fio, EXTRACT(YEAR FROM S_Birthday)
FROM Sotrudniki
WHERE EXTRACT(YEAR FROM S_Birthday)=
(SELECT max(EXTRACT(YEAR FROM S_Birthday))
FROM Sotrudniki)
- вложенный оператор SELECT возвращает максимальный год рождения, который используется в условии WHERE основного оператора SELECT.
3) Вывести из БД Students все оценки конкретного студента, например, Петрова:
SELECT S_fam, P_nazv, E_mark FROM
Examination,Predm, Students
WHERE E_student=(SELECT S_code FROM Students
WHERE S_fam='Петров')
AND E_Predm=P_code AND E_Student=S_code
- во вложенной конструкции SELECT определяется код студента по фамилии 'Петров', а последние условия обеспечивают исключение избыточности при внутреннем объединении таблиц.
Связанные подзапросы. Во внутреннем запросе можно ссылаться на таблицу, имя которой указано в предложении FROM внешнего запроса. Такой связанный подзапрос выполняется по одному разу для каждой строки таблицы основного запроса.
Например, получить сведения о предметах, по которым проводился экзамен конкретного числа, например, ‘14.01.2006’:
SELECT * FROM Predm PR
WHERE '14.01.2006' IN (SELECT E_date
FROM Examination
WHERE PR.P_code=E_predm)
Эту же задачу можно решить с помощью операции соединения таблиц:
SELECT DISTINCT P_nazv FROM Predm, Examination
WHERE P_code=E_predm AND E_date= '14.01.2006'
p
Пример. Вывести фамилию (фамилии) студента, получившего на экзамене оценку выше среднего балла
SELECT DISTINCT S_fam FROM Students, Examination
WHERE
E_mark>(SELECT AVG(E_mark) FROM Examination)
AND S_code=E_student
p
В условии WHERE при работе с множествами записей можно использовать ключевые слова ALLи ANY. ALL- условие выполняется для всех записей, ANY- условие выполняется хотя бы для одной записи.
Например,
1) вывести фамилии сотрудников из таблицы Sotrudniki, которые не старше любого сотрудника в таблице Sotrudniki1:
SELECT S_fio,S_birthday FROM Sotrudniki
WHERE S_birthday>= ALL (SELECT S_birthday
FROM Sotrudniki1)
2) вывести фамилии сотрудников из таблицы Sotrudniki, которые моложе хотя бы одного сотрудника в таблице Sotrudniki1:
SELECT S_fio,S_birthday FROM Sotrudniki
WHERE S_birthday> ANY (SELECT S_birthday FROM Sotrudniki1)
p
Во вложенных конструкциях SELECT можно использовать ключевое слово EXISTS, которое означает отбор только тех записей, для которых вложенный запрос возвращает одно или более значений.
Например,
SELECT S_fio,S_birthday FROM Sotrudniki S1
WHERE EXISTS (SELECT S_fio,S_birthday
FROM Sotrudniki S2
WHERE (S1.S_birthday=S2.S_birthday)
AND (S1.S_code!=S2.S_code))
- получение списка сотрудников, которые имеют хотя бы одного сверстника.