Раздел 7. Запросы с соединением
Ранее рассматривались запросы, которые извлекали данные из одной таблицы, но более интересным и имеющим большое практическое применение является выборка в запросе связанных данных из нескольких таблиц. Рассматриваемые далее примеры используют базу данных, схема которой приводится ниже. Она содержит данные о студентах, преподавателях, и читаемых курсах. CREATE TABLE Student -- студенты (name CHAR(20), -- имя студента class INT); -- группа
CREATE TABLE Course -- читаемые курсы (title CHAR(20), -- название курса staff CHAR(20)); -- имя преподавателя
CREATE TABLE Staff -- преподаватели (name CHAR(20), -- имя преподавателя position CHAR(20)); -- должность
CREATE TABLE Attends -- посещаемые курсы (student CHAR(20), -- имя студента title CHAR(20)); -- название курса Ниже приведены данные, содержащиеся в каждой из таблиц.
Рассмотрим запрос, который осуществляет выборку строк из двух таблиц: SELECT * FROM Course, Attends; (7.1)
С точки зрения формальной семантики языка SQL, значение данного запроса образовано следующим образом. Каждая строка таблицы Course (всего 5 строк) “объединяется” с каждой строкой из таблицы Attends (5 строк); всего получается 5*5 строк в результирующем множестве. Подобное действие, получившее название соединение двух таблиц, (точнее говоря, в данном случае - кросс-сединением), похоже на математическую операцию декартового произведения двух множеств. Этот запрос, однако, является десемантизированным (бессмысленным), поскольку с содержательной точки зрения, его формальное значение – результирующее множество – представляет собой механически построенный набор полей, не имеющий какого-либо содержания. Более продуктивным является использование запросов следующего вида: SELECT Attends.student, Course.title, Course.staff FROM Course, Attends WHERE Course.title = Attends.title; (7.2)
Этот запрос уже имеет вполне осмысленный результат – имена студентов, названия посещаемых ими курсов и фамилии преподавателей. Обратите внимание, что в исходной базе данных не существует таблицы, содержащей одновременно эти три поля. Важнейшее назначение многотабличных запросов – извлечение данных из строк, между которыми подразумевается некоторая связь, и которая обычно подразумевает совпадение значений некоторых полей в обеих таблицах, как поля title из таблицы Course и title из Attends. Впрочем, язык SQL допускает использование любых условий при многотабличных запросах, помимо проверки на равенство, но такие запросы будут в большинстве случаев бессмысленны. В этом запросе имена полей квалифицированы именами таблиц, из которых эти поля извлекаются; это способствует наглядности запросов, и бывает необходимым для устранения неоднозначности, если в соединяемых таблицах существуют одноименные поля.
Формальное вычисление значение запроса, называемого запросом с эквисоединением двух таблиц, можно представить как (1) вычисление набора всех возможных пар строк из обеих таблиц и (2) удаление из этого набора строк, не удовлетворяющих условию WHERE. Запросы 7.1 и 7.2 можно записать в соответствии с более современным синтаксисом языка, используя конструкцию (INNER) JOIN: SELECT Attends.student, Course.title, Course.staff FROM Course CROSS JOIN Attends; (7.3) SELECT Attends.student, Course.title, Course.staff FROM Course JOIN Attends ON Course.title = Attends.title; (7.4) SELECT student, title, staff FROM Course JOIN Attends USING (title); (7.5) SELECT title, staff, student FROM Course NATURAL JOIN Attends; (7.6) Запрос 7.5 использует конструкцию JOIN USING, предполагающую соединение двух таблиц по совпадающим именам полей в таблицах; вариант соединения в запросе 7.6 называется естественным соединением (NATURAL JOIN), при этом в паре таблиц предполагается наличие одноименных полей. При использовании естественного соединения и конструкции JOIN USING имена полей в выражениях после SELECT не могут быть квалифицированными. Конструкция CROSS JOIN в запросе 7.3 реализует декартово произведение. Рассмотрим трех- и четырехтабличные запросы: SELECT Student.name, Student.class, Attends.title, Course.staff FROM Student, Attends, Course WHERE Student.name = Attends.student AND Attends.title = Course.title; (7.7)
SELECT Student.name, Student.class, Attends.title, Course.staff, Staff.position FROM Student, Attends, Course, Staff WHERE Student.name = Attends.student AND Attends.title = Course.title AND Course.staff = Staff.name; (7.8)
Запрос 7.7 с использованием JOIN ON записывается так: SELECT Student.name, Student.class, Attends.title, Course.staff FROM (Student JOIN Attends ON Student.name = Attends.student) JOIN Course ON (Attends.title = Course.title); (7.9) Скобки в последнем примере не обязательны и использованы для наглядности текста. Вернемся к запросу 7.2. В выборке запись, соответствующая записи «Cryptography Won Kim» из таблицы Course отсутствует, поскольку в таблице Attends нет ни одной записи со значением Cryptography в поле Course. Если в результирующем множестве необходимо присутствие таких “несвязанных” записей, следует использовать так называемые несимметричные соединения - правое (RIGHT JOIN) или левое (LEFT JOIN) внешние соединения, или полное соединение, в отличие от симметричного (внутреннего) соединения. Следующие два эквивалентных примера иллюстрируют использование левого соединения:
SELECT Student.name, Attends.title FROM Student LEFT JOIN Attends ON Student.name = Attends.student (7.10) SELECT Student.name, Attends.title FROM Student, Attends WHERE Student.name = Attends.student (+) (7.11)
В последнем запросе 7.11 используется архаичная запись операции левого соединения, характерная для ранних версий языка SQL в системе Oracle. В результирующем множестве строки действительно присутствует строка, соответствующая записи “Gill” исходной таблицы Student, а значение поля Title, для которого значения в таблице Course не существует, получило формальное значение NULL. Ниже приведен пример правого соединения: SELECT student, title, staff FROM Attends RIGHT JOIN Course USING (title); (7.12)
Наконец, приведем пример полного соединения (FULL JOIN), которое предполагает наличие «несвязанные» записей в обеих таблицах. SELECT name, title, staff FROM Student LEFT JOIN Attends ON Student.name = Attends.student FULL JOIN Course USING (title); (7.13) В этом запросе сначала образуется левое соединение таблиц Student и Attends по полям name и student соответственно, что, как видно из примера 7.10, приводит к появлению в выборке пустых значений в поле title. Затем выполняется полное соединение полученной выборки с таблицей Course:
Несимметричные соединения полезны для выявления «несвязанных» записей; этот тип запросов с соединением часто называется антисоединением. Следующий характерный пример определяет количество студентов, посещающих проводимые курсы, включая курсы, которые никто не посещает: SELECT title "Курс", staff "Преподавателя", COUNT(student) "Посещают,чел." FROM Attends RIGHT JOIN Course USING (title)
GROUP BY title, staff; (7.14)
Модифицируя предыдущий запрос, можно получить название курса, не посещаемого студентами: SELECT title "Курс", staff "Преподаватель" FROM Attends RIGHT JOIN Course USING (title) GROUP BY title, staff HAVING COUNT(student) = 0; (7.15)
Соединение таблиц может выполняться не только по одному полю, но и по комбинации полей (два или более). Рассмотрим, например, следующий запрос: SELECT A.student, A.title, CR.result FROM Attends A JOIN CourseResult CR ON CR.student = A.student AND CR.Course = A.title (7.16)
В этом запросе соединение таблиц Attends и CourseResult происходит по комбинации полей – имя студента и название курса, поскольку каждое из этих полей по отдельности не идентифицирует соответствующую запись в этих таблицах. Это свойство данных определяется предположением, что один студент может посещать несколько курсов, и наоборот, – один курс могут посещать несколько студентов. Кроме того, один и тот же студент не может посещать один и тот же курс «дважды» и, соответственно, в таблицах Attends и CourseResult не может быть более чем по одной записи с одинаковыми именами студента и названием курса. Поэтому при соединении действительно необходимо использовать сравнение по комбинации полей. Однако, с формальной точки зрения нет основания считать, что каждой записи в таблице Attends обязательно должна соответствовать запись в таблице CourseResult (хотя это было бы естественное предположение). Если такая ситуация возможна, то запрос 7.16 должен использовать соединение этих двух таблиц. Если в запросе с соединением некоторая таблица в конструкции FROM используется два и более раз, такой запрос называется запросом с самосоединением. Такие запросы удобны для работы с рекурсивными данными, представленными в виде записей в SQL-таблице. Например, в следующем примере в таблице "Трубы" представлена топология газовых коммуникаций, которая имеет древовидную структуру:
CREATE TABLE "Трубы" ("Паспорт" CHAR(100), "Номер" INT, "Отвод из" INT);
Рисунок 1. Топология газовых коммуникаций
Запрос 7.17 формирует список коммуникаций между трубами: SELECT Branch."Паспорт" || ' выходит из ' || MainPipe."Паспорт" "Коммуникации" FROM "Трубы" MainPipe RIGHT JOIN "Трубы" Branch ON MainPipe."Номер" = Branch."Отвод"; (7.17)
В этом запросе используемые имена MainPipe и Branch называются синонимами. Любую таблицу можно снабдить синонимом; обычно это делается, чтобы избежать в выражениях запроса использования длинных имен исходных таблиц. В случае запросов с самосоединением использование синонимов необходимо для избежания неоднозначности. Список, созданный запросом 7.18, можно улучшить при помощи оператора CASE (см. раздел 9): SELECT 'Труба ' || Branch."Паспорт" || CASE WHEN MainPipe."Паспорт" IS NULL THEN ' начальная' ELSE ' выходит из ' || MainPipe."Паспорт" END "Коммуникации" FROM "Трубы" MainPipe RIGHT JOIN "Трубы" Branch ON MainPipe."Номер" = Branch."Отвод" ORDER BY Branch."Отвод" DESC; (7.17)
Раздел 8. Подзапросы SQL-запросы могут содержать вложенные SELECT-выражения, называемые подзапросами. Подзапросы используются, в том числе, в логических выражениях в конструкциях WHERE и HAVING, и в качестве «динамических» таблиц в конструкции FROM.
Воспользуйтесь поиском по сайту: ©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...
|