УП СУБД ч1
.pdf
|
COUNT(*) |
|
COUNT(RESULT) |
|
|
||
|
5 |
|
|
3 |
|
|
|
|
Рис. 10.7. Значение запроса 10.9 |
|
|||||
SELECT SUM(result) FROM CourseResult; |
(10.10) |
||||||
|
|
|
|
|
|
||
|
|
|
SUM(RESULT) |
|
|
|
|
|
|
|
14 |
|
|
|
Рис. 10.8. Значение запроса 10.10
При группировке по некоторому полю пустые значения в этом поле также не игнорируется:
SELECT result, COUNT(result), COUNT(*) |
|
(10.11) |
||||
FROM CourseResult GROUP BY result; |
||||||
|
|
|
|
|
|
|
|
RESULT |
COUNT(RESULT) |
|
COUNT(*) |
|
|
|
- |
0 |
|
|
2 |
|
|
5 |
2 |
|
|
2 |
|
|
4 |
1 |
|
|
1 |
|
Рис. 10.9. Значение запроса 10.11
При работе с пустыми значениями используются функции NVL() и NVL2(). Функция NVL() имеет два параметра; значение этой функции – значение первого параметра, если это не пустое значение, или значение второго параметра. Функция NVL2() имеет три параметра; если значение первого параметра – NULL, то функция возвращает значение третьего параметра или значение второго параметра – в противном случае. Ниже приведены примеры использования этих функций.
SELECT STUDENT, COURSE, NVL(TO_CHAR(RESULT), ‘без оценки’)
FROM CourseResult |
(10.12) |
||
|
|
|
|
STUDENT |
COURSE |
|
NVL(TO_CHAR(RESULT),’БЕЗОЦЕНКИ’) |
Jane |
OS |
|
5 |
Bill |
C Programming |
|
5 |
Polie |
Graphics |
|
без оценки |
Jane |
DBMS |
|
4 |
Bill |
DBMS |
|
без оценки |
Рис. 10.10. Значение запроса 10.12
101
SELECT STUDENT, COURSE, NVL2(RESULT, ‘хорошо’, ‘плохо’)
FROM CourseResult |
(10.13) |
|
|
|
|
STUDENT |
COURSE |
NVL2(RESULT,’ХОРОШО’,’ПЛОХО’) |
Jane |
OS |
Хорошо |
Bill |
C Programming |
Хорошо |
Polie |
Graphics |
Плохо |
Jane |
DBMS |
Хорошо |
Bill |
DBMS |
Плохо |
Рис. 10.11. Значение запроса 10.13
10.2. Запросы с соединением
Ранее рассматривались запросы, которые извлекали данные из одной таблицы, но более интересным и имеющим большое практическое применение является выборка в запросе связанных данных из нескольких таблиц. Рассматриваемые далее примеры используют базу данных, схема которой приводится ниже. Она содержит данные о студентах, преподавателях и читаемых курсах.
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)); |
– |
название курса |
|
|
|
|
|
|
|
|
NAME |
CLASS |
|
|
|
Gill |
1 |
|
|
|
Jane |
3 |
|
Bill 3
Polie 2
102
|
NAME |
POSITION |
|
Won Kim |
Full Professor |
|
G.Anderson |
Senior Lector |
|
S.Abiteboul |
Full Professor |
|
|
|
|
TITLE |
STAFF |
|
DBMS |
G.Anderson |
|
Graphics |
G.Anderson |
|
OS |
G.Anderson |
|
C Programming |
Won Kim |
|
Cryptography |
Won Kim |
|
|
|
|
STUDENT |
TITLE |
|
Jane |
DBMS |
|
Bill |
DBMS |
|
Jane |
OS |
|
Bill |
C Programming |
|
Polie |
Graphics |
Рис. 10.12. Таблицы Student, Course, Staff и Attends
Рассмотрим запрос, который осуществляет выборку из двух таблиц:
SELECT * FROM Course, Attends; |
(10.12) |
||
|
|
|
|
TITLE |
STAFF |
STUDENT |
TITLE |
DBMS |
G.Anderson |
Jane |
DBMS |
DBMS |
G.Anderson |
Bill |
DBMS |
DBMS |
G.Anderson |
Jane |
OS |
DBMS |
G.Anderson |
Bill |
C Programming |
DBMS |
G.Anderson |
Polie |
Graphics |
Graphics |
G.Anderson |
Jane |
DBMS |
Graphics |
G.Anderson |
Bill |
DBMS |
Graphics |
G.Anderson |
Jane |
OS |
Graphics |
G.Anderson |
Bill |
C Programming |
Graphics |
G.Anderson |
Polie |
Graphics |
OS |
G.Anderson |
Jane |
DBMS |
OS |
G.Anderson |
Bill |
DBMS |
OS |
G.Anderson |
Jane |
OS |
OS |
G.Anderson |
Bill |
C Programming |
OS |
G.Anderson |
Polie |
Graphics |
C Programming |
Won Kim |
Jane |
DBMS |
Рис. 10.13. Значение запроса 10.12
103
TITLE |
STAFF |
STUDENT |
TITLE |
C Programming |
Won Kim |
Bill |
DBMS |
C Programming |
Won Kim |
Jane |
OS |
C Programming |
Won Kim |
Bill |
C Programming |
C Programming |
Won Kim |
Polie |
Graphics |
Cryptography |
Won Kim |
Jane |
DBMS |
Cryptography |
Won Kim |
Bill |
DBMS |
Cryptography |
Won Kim |
Jane |
OS |
Cryptography |
Won Kim |
Bill |
C Programming |
Cryptography |
Won Kim |
Polie |
Graphics |
Рис. 10.13. (продолжение)
С точки зрения формальной семантики языка SQL значение данного запроса образовано следующим образом. Каждая строка таблицы Course (всего 5 строк) «объединяется» с каждой строкой из таблицы Attends (5 строк); всего получается 5*5 строк в результирующем множестве. Подобное действие, получившее название соединение двух таблиц, (точнее говоря, в данном случае – кросс-сединение), похоже на математическую операцию декартового произведения двух множеств. Этот запрос, однако, является десемантизированным (бессмысленным), поскольку с содержательной точки зрения его формальное значение – результирующее множество представляет собой механически построенный набор полей, не имеющий какого-либо содержания.
Более продуктивным является использование запросов следующего вида:
SELECT Attends.student, Course.title, Course.staff
FROM Course, Attends |
(10.13) |
||||
WHERE Course.title = Attends.title; |
|||||
|
|
|
|
|
|
|
STUDENT |
TITLE |
|
STAFF |
|
|
Jane |
DBMS |
|
G.Anderson |
|
|
Bill |
DBMS |
|
G.Anderson |
|
|
Jane |
OS |
|
G.Anderson |
|
|
Bill |
C Programming |
|
Won Kim |
|
|
Polie |
Graphics |
|
G.Anderson |
|
Рис. 10.14. Значение запроса 10.13
104
Этот запрос уже имеет вполне осмысленный результат – имена студентов, названия посещаемых ими курсов и фамилии преподавателей. Обратите внимание, что в исходной базе данных не существует таблицы, содержащей одновременно эти три поля. Важнейшее назначение многотабличных запросов – извлечение данных из строк, между которыми подразумевается некоторая связь, которая обычно подразумевает совпадение значений некоторых полей в обеих таблицах, как поля title из таблицы Course и title из Attends. Впрочем, язык SQL допускает использование любых условий при многотаб личных запросах, помимо проверки на равенство, но такие запросы будут в большинстве случаев бессмысленны.
В этом запросе имена полей квалифицированы именами таблиц, из которых эти поля извлекаются; это способствует наглядности запросов и бывает необходимым для устранения неоднозначности, если в соединяемых таблицах существуют одноименные поля.
Формальное вычисление значения запроса, называемого запросом с эквисоединением двух таблиц, можно представить как: 1) вычисление набора всех возможных пар строк из обеих таблиц; 2) удаление из этого набора строк, не удовлетворяющих условию WHERE.
Запросы 10.12 и 10.13 можно записать в соответствии с более современным синтаксисом языка, используя конструкцию (INNER) JOIN:
SELECT Attends.student, Course.title, Course.staff |
(10.14) |
FROM Course CROSS JOIN Attends; |
|
SELECT Attends.student, Course.title, Course.staff |
|
FROM Course JOIN Attends |
(10.15) |
ON Course.title = Attends.title; |
|
SELECT student, title, staff |
(10.16) |
FROM Course JOIN Attends USING (title); |
|
SELECT title, staff, student |
(10.17) |
FROM Course NATURAL JOIN Attends; |
Запрос 10.16 использует конструкцию JOIN USING, предполагающую соединение двух таблиц по совпадающим именам полей в таблицах; вариант соединения в запросе 10.17 называется естественным соединением (NATURAL JOIN), при этом в паре таблиц предполагается наличие одноименных полей.
105
При использовании естественного соединения и конструкции JOIN USING имена полей в выражениях после SELECT не могут быть квалифицированными. Конструкция CROSS JOIN в запросе 10.14 реализует декартово произведение.
Рассмотрим трех- и четырехтабличные запросы:
SELECT Student.name, Student.class, Attends.title,
|
Course.staff |
|
|
|
|
FROM Student, Attends, Course |
|
|
|||
WHERE Student.name = Attends.student |
|||||
AND Attends.title = Course.title; |
(10.18) |
||||
|
|
|
|
|
|
NAME |
CLASS |
|
TITLE |
STAFF |
|
Bill |
3 |
|
DBMS |
G.Anderson |
|
Jane |
3 |
|
DBMS |
G.Anderson |
|
Polie |
2 |
|
Graphics |
G.Anderson |
|
Jane |
3 |
|
OS |
G.Anderson |
|
Bill |
3 |
|
C Programming |
Won Kim |
|
|
|
Рис. 10.15. Значение запроса 10.18 |
|
|
|||
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 |
|
(10.19) |
|||
|
AND Course.staff = Staff.name; |
|
|||||
|
|
|
|
|
|
|
|
NAME |
|
CLASS |
TITLE |
STAFF |
|
POSITION |
|
Jane |
|
3 |
OS |
G.Anderson |
|
Senior Lector |
|
Jane |
|
3 |
DBMS |
G.Anderson |
|
Senior Lector |
|
Bill |
|
3 |
C Programming |
Won Kim |
|
Full Professor |
|
Bill |
|
3 |
DBMS |
G.Anderson |
|
Senior Lector |
|
Polie |
|
2 |
Graphics |
G.Anderson |
|
Senior Lector |
|
|
|
Рис. 10.16. Значение запроса 10.19 |
|
|
|||
Запрос 10.19 с использованием 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 ); |
(10.20) |
106
Скобки в последнем примере необязательны и использованы для наглядности текста.
Вернемся к запросу 10.13. В выборке запись, соответствующая записи «Cryptography Won Kim» из таблицы Course, отсутствует, поскольку в таблице Attends нет ни одной записи со значением Cryptography в поле Course. Если в результирующем множестве необходимо присутствие таких «несвязанных» записей, следует использовать так называемые несимметричные соединения – правое (RIGHT JOIN) или левое (LEFT JOIN) внешние соединения, или полное соединение, в отличие от симметричного (внутреннего) соединения. Следующие два эквивалентных примера иллюстрируют использование левого соединения:
SELECT Student.name, Attends.title |
|
|||
FROM Student LEFT JOIN Attends |
(10.21) |
|||
ON Student.name = Attends.student |
||||
SELECT Student.name, Attends.title |
|
|||
FROM Student, Attends |
(10.22) |
|||
WHERE Student.name = Attends.student (+) |
||||
|
|
|
|
|
|
NAME |
TITLE |
|
|
|
Jane |
DBMS |
|
|
|
Bill |
DBMS |
|
|
|
Jane |
OS |
|
|
|
Bill |
C Programming |
|
|
|
Polie |
Graphics |
|
|
|
Gill |
- |
|
|
Рис. 10.17. Значения запроса 10.21 и 10.22
Впоследнем запросе 10.22 используется архаичная запись операции левого соединения, характерная для ранних версий языка SQL в системе Oracle.
Врезультирующем множестве строки действительно присутствует строка, соответствующая записи “Gill” исходной таблицы Student, а значение поля Title, для которого значение в таблице Course не существует, получило формальное значение NULL. Ниже приведен пример правого соединения:
SELECT student, title, staff |
|
FROM Attends RIGHT JOIN Course |
(10.23) |
USING (title); |
107
STUDENT |
TITLE |
STAFF |
Jane |
DBMS |
G.Anderson |
Bill |
DBMS |
G.Anderson |
Jane |
OS |
G.Anderson |
Bill |
C Programming |
Won Kim |
Polie |
Graphics |
G.Anderson |
- |
Cryptography |
Won Kim |
Рис. 10.18. Значение запроса 10.23
Наконец, приведем пример полного соединения (FULL JOIN), которое предполагает наличие «несвязанных» записей в обеих таблицах:
SELECT name, title, staff |
|
FROM Student LEFT JOIN Attends |
|
ON Student.name = Attends.student |
(10.24) |
FULL JOIN Course USING (title); |
В этом запросе сначала образуется левое соединение таблиц Student и Attends по полям name и student соответственно, что, как видно из примера 10.23, приводит к появлению в выборке пустых значений в поле title. Затем выполняется полное соединение полученной выборки с таблицей Course:
NAME |
TITLE |
STAFF |
Bill |
DBMS |
G.Anderson |
Jane |
DBMS |
G.Anderson |
Polie |
Graphics |
G.Anderson |
Jane |
OS |
G.Anderson |
Bill |
C Programming |
Won Kim |
Gill |
- |
- |
- |
Cryptography |
Won Kim |
Рис. 10.19. Значение запроса 10.24
Несимметричные соединения полезны для выявления «несвязанных» записей; этот тип запросов с соединением часто называется антисоединением. Следующий характерный пример определяет количество студентов, посещающих проводимые курсы, включая курсы, которые никто не посещает:
108
SELECT title «Курс», staff «Преподаватель» |
|
FROM Attends RIGHT JOIN Course USING (title) |
(10.25) |
WHERE student IS NULL; |
Аналогичный результат можно получить и следующим образом:
SELECT title «Курс», staff «Преподавателя», COUNT(student) «Посещают,чел.»
FROM Attends RIGHT JOIN Course USING (title)
GROUP BY title, staff; |
(10.26) |
||
|
|
|
|
Курс |
Преподавателя |
Посещают, чел. |
|
OS |
G.Anderson |
1 |
|
Graphics |
G.Anderson |
1 |
|
C Programming |
Won Kim |
1 |
|
DBMS |
G.Anderson |
2 |
|
Cryptography |
Won Kim |
0 |
|
Рис. 10.20. Значение запроса 10.26
Модифицируя предыдущий запрос, можно получить название курса, не посещаемого студентами:
SELECT title «Курс», staff «Преподаватель» FROM Attends RIGHT JOIN Course USING (title)
GROUP BY title, staff |
|
(10.26) |
|
|
HAVING COUNT(student) = 0; |
||
|
|
|
|
|
Курс |
Преподаватель |
|
|
|
|
|
|
Cryptography |
Won Kim |
|
|
|
|
|
Рис. 10.21. Значение запроса 10.26
Соединение таблиц может выполняться не только по одному полю, но и по комбинации полей (два или более). Рассмотрим, например, следующий запрос:
SELECT A.student, A.title, CR.result |
|
FROM Attends A JOIN CourseResult CR |
|
ON CR.student = A.student |
(7.27) |
AND CR.Course = A.title |
109
STUDENT |
TITLE |
RESULT |
Bill |
C Programming |
5 |
Jane |
OS |
5 |
Jane |
DBMS |
4 |
Bill |
DBMS |
- |
Polie |
Graphics |
- |
Рис. 10.22. Значение запроса 10.27
В этом запросе соединение таблиц Attends и CourseResult происходит по комбинации полей «имя студента» и «название курса», поскольку каждое из этих полей по отдельности не идентифицирует соответствующую запись в этих таблицах. Это свойство данных определяется предположением, что один студент может посещать несколько курсов, и наоборот, один курс могут посещать несколько студентов. Кроме того, один и тот же студент не может посещать один и тот же курс «дважды», и, соответственно, в таблицах Attends и CourseResult не может быть более чем по одной записи с одинаковыми именами студента и названием курса. Поэтому при соединении действительно необходимо использовать сравнение по комбинации полей.
Однако с формальной точки зрения нет основания считать, что каждой записи в таблице Attends обязательно должна соответствовать запись в таблице CourseResult (хотя это было бы естественным предположением). Если такая ситуация возможна, то запрос 7.27 должен использовать соединение этих двух таблиц.
Если в запросе с соединением некоторая таблица в конструкции FROM используется два и более раз, такой запрос называется запросом с самосоединением. Такие запросы удобны для работы с рекурсивными данными, представленными в виде записей в SQL-таблице. Например, в следующем примере в таблице «Трубы» представлена топология газовых коммуникаций, которая имеет древовидную структуру:
CREATE TABLE «Трубы» |
Паспорт |
Номер |
Отвод из |
( «Паспорт» CHAR(100), |
ТВД-12А |
1 |
- |
«Номер» INT, |
ТСД-234А1 |
2 |
1 |
«Отвод из» INT ); |
ТСД-32И |
3 |
2 |
|
ТНД-43В2 |
4 |
3 |
|
ТСД-73 |
5 |
1 |
Рис. 10.23. Данные таблицы «Трубы»
110