Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

УП СУБД ч1

.pdf
Скачиваний:
17
Добавлен:
11.06.2015
Размер:
1.05 Mб
Скачать

STUDENT

COURSE

RESULT

Bill

C Programming

5

Jane

OS

5

Рис. 11.3. Значение запроса 11.9

Подзапросы можно использовать и в операциях сравнения. Например, запрос 11.9 определяет, какие студенты посещают более одного курса:

SELECT S.name, S.class FROM Student S

 

WHERE ( SELECT COUNT(*) FROM Attends

(11.10)

WHERE student = S.name ) > 1;

 

 

 

 

 

 

NAME

CLASS

 

 

 

Jane

3

 

 

 

Bill

3

 

 

Рис. 11.4. Значение запроса 11.10

Запрос 11.11 позволяет определить, какие студенты посещают более одного курса, читаемого преподавателем

‘G.Anderson’:

SELECT S.name, S.class FROM Student S

WHERE ( SELECT COUNT(*) FROM Attends A

WHERE A.student = S.name AND ‘G.Anderson’ IN

( SELECT staff FROM Course

WHERE A.title = title ) )> 1; (11.11)

NAME CLASS

Jane 3

Рис. 11.5. Значение запроса 11.11

На использование подзапросов в операциях сравнения накладываются некоторые дополнительные ограничения. В качестве примера рассмотрим два схожих запроса с некоррелированными подзапросами:

SELECT title, staff from Course WHERE staff =

 

( SELECT name FROM Staff

(11.12)

WHERE position = ‘Senior Lector’ );

121

SELECT title, staff from Course WHERE staff =

 

( SELECT name FROM Staff

(11.13)

WHERE position = ‘Full Professor’ );

Оба запроса являются синтаксически корректными, и запрос 11.12 действительно будет выполнен, сформировав следующую выборку:

TITLE

STAFF

DBMS

G.Anderson

Graphics

G.Anderson

OS

G.Anderson

Рис. 11.6. Значение запроса 11.13

Но при исполнении запроса 11.13 (при текущем состоянии данных в таблице Staff) возникнет следующая ошибка:

ORA-01427: single-row subquery returns more than one row.

Это означает, что выборка подзапроса содержит более одной строки; сравнение одиночных значений с множествами в языке SQL не предусмотрено.

Таким образом, при использовании подзапросов в операциях сравнения необходимо, чтобы: 1) значение подзапроса состояло не более чем из одной записи; 2) эта запись была совместима по типам полей со сравниваемым значением. Кстати, запись не обязана быть атомарным значением (т.е. состоять из одного поля); например, можно использовать следующий (несколько искусственный) вариант, использующий сравнение списочных выражений, одно из которых является подзапросом:

SELECT title, staff from Course

 

WHERE (staff, ‘Senior Lector’ ) =

 

( SELECT name, position FROM Staff

(11.14)

WHERE position = ‘Senior Lector’ )

Возвращаясь к запросам 8.12 и 8.13, следует отметить, что подобный тип запросов является не вполне удачным, поскольку его исполнение существенно зависит от конкретных значений используемых параметров (значения, сравниваемого с полем position) и текущего состояния таблицы Staff. Более безопасным является использование эквивалентного

122

варианта с предикатом IN (11.15) или с коррелированным подзапросом (11.16), которые для любых данных будут работать корректно:

SELECT title, staff from Course WHERE staff IN

 

( SELECT name FROM Staff

(11.15)

WHERE position = ‘Full Professor’ );

SELECT title, staff from Course WHERE staff IN

( SELECT name FROM Staff WHERE Staff.name = staff

AND position = ‘Full Professor’ );

(11.16)

Запрос 11.15 с предикатом IN является более предпочтительным, поскольку запросы с некоррелированными подзапросами выполняются более эффективно.

Еще одним полезным предикатом является предикат EXISTS, который в качестве параметра получает подзапрос и возвращает значение TRUE, если результирующее множество подзапроса содержит хотя бы одну запись.

Следующий запрос определяет, по каким курсам существуют «задолжники»:

SELECT staff, title FROM Course C WHERE EXISTS

 

( SELECT * FROM CourseResult

 

WHERE course = C.title AND

(11.17)

( result IS NULL OR result < 3 ) )

 

 

 

 

 

 

STAFF

TITLE

 

 

 

G.Anderson

Graphics

 

 

 

G.Anderson

DBMS

 

 

Рис. 11.7. Значение запроса 11.17

При использовании подзапросов совместно с предикатом EXISTS конкретный вид выражений после SELECT в подзапросе никакого значения не имеет; однако было бы ошибкой использовать, например, функцию COUNT(*), поскольку в этом случае подзапрос всегда содержал бы одну строку.

При помощи предиката EXISTS удобно находить «несвязанные» записи (т.е. выполнять неявное антисоединение). Ниже приведен подобный запрос, эквивалентный запросу 10.17:

123

SELECT C.title «Курс», staff «Преподавателя»

 

FROM Course C

 

WHERE NOT EXISTS

 

( SELECT * FROM Attends A

(11.18)

WHERE C.title = A.title)

В сравнении с запросом 10.17 текст данного запроса выглядит более очевидным.

С подзапросами связано использование двух предикатов ANY и ALL, которые употребляются в операторах сравнения и в качестве параметра, аналогично предикату IN, используют подзапрос или списочное выражение. Например, следующий запрос определяет студентов с оценкой, которая не является минимальной из существующих (по любому курсу):

SELECT student, course, result FROM CourseResult

WHERE result > ANY

(11.19)

( SELECT result FROM CourseResult )

 

 

 

 

 

 

 

STUDENT

COURSE

 

RESULT

 

 

 

 

 

 

 

 

Bill

C Programming

 

5

 

 

Jane

OS

 

5

 

Рис. 11.8. Значение запроса 10.19

При вычислении этого запроса сначала вычисляется подзапрос, который является некоррелированным, и формируется список значений, а затем для каждой строки проверяется предикат ANY; если значение поля result текущей записи больше хотя бы одного значения из списка, то значением операции сравнения является TRUE. Обратите внимание, что пустые значения, которые присутствуют в значении подзапроса SELECT result FROM CourseResult, игнорируются.

Может показаться, что запрос 11.19 определяет студентов с максимальной оценкой. Однако это верно только применительно к текущим данным в таблице CourseResult. В ней, за исключением игнорируемых пустых значений в поле result, встречаются только значения 4 и 5, хотя возможно и появление значения 3. В этом случае значение запроса 11.19 не будет совпадать со значением запроса 11.9, который всегда будет возвращать правильный результат. Данное наблюдение иллюстрирует тот факт, что «правильность» некоторого

124

SQL-запроса трудно оценить только методом тестирования, вычисляя его с некоторыми конкретными данными.

Предикат ALL, в отличие от предиката ANY, требует, чтобы условие сравнения выполнялось для всех значений из списка – значений подзапроса. В качестве примера построим запрос для нахождения студентов с максимальной оценкой аналогично запросу 11.9.

SELECT student, course, result FROM CourseResult CR WHERE result >= ALL

( SELECT result FROM CourseResult CR2

HERE CR2.result IS NOT NULL )

(11.20)

 

 

 

 

 

STUDENT

COURSE

 

RESULT

 

Bill

C Programming

 

5

 

Jane

OS

 

5

 

Рис. 11.9. Значение запроса 11.20

Обратите внимание, что в запросе 11.20 подзапрос использует ту же самую таблицу, что и основной запрос, поэтому здесь необходимо использовать оператор сравнения >=, поскольку значение этого подзапроса в данном случае всегда включает значение result из левой части. Данный запрос можно переписать таким образом, чтобы для каждой записи внешней таблицы исключать из данных подзапроса эту запись; для этого будет необходимо использовать коррелированный подзапрос. Кроме того, этот запрос определяет максимальную оценку вне зависимости от курса; если необходимо получить максимальные оценки по каждому курсу в отдельности, запрос приобретет существенно более сложный вид:

SELECT student, course, result FROM CourseResult CR WHERE result > ALL

( SELECT result FROM CourseResult CR2 WHERE NOT ( CR.student = CR2.student AND CR.course =

CR2.course )

 

AND CR.course = CR2.course

 

AND CR2.result IS NOT NULL )

(11.21)

AND CR.result IS NOT NULL

125

STUDENT

COURSE

RESULT

 

 

 

Bill

C Programming

5

Jane

OS

5

Jane

DBMS

4

Рис. 11.10. Значение запроса 11.21

Прежде всего, необходимо обратить внимание на условие

CR.student = CR2.student AND CR.course = CR2.course, которое обычно используется как условие соединения двух таблиц по паре полей. Но в данном случае это условие используется с отрицанием, поскольку цель этого коррелированного подзапроса – построить неявное кросс-соединение таблицы с собой, удаляя для каждой записи внешней таблицы эту же запись из внутренней таблицы, чтобы избежать сравнения записи самой с собой. Проверка CR.result IS NOT NULL необходима, чтобы результирующее множество не содержало сведений о курсах, по которым не выставлено ни одной оценки. Наконец, условие CR.course = CR2.course (пятая строка текста запроса) необходимо, чтобы подзапрос каждый раз рассматривал только записи с одинаковыми названиями курса. Текст данного запроса можно переписать в более короткую форму, упростив логическое выражение (читателю рекомендуется самостоятельно выполнить шаги преобразования этого логического выра­жения):

SELECT student, course, result FROM CourseResult CR

WHERE result > ALL

 

( SELECT result FROM CourseResult CR2 WHERE

 

CR.student != CR2.student

 

AND CR.course = CR2.course

 

AND CR2.result IS NOT NULL )

(11.22)

AND CR.result IS NOT NULL

В конце подраздела рассмотрим еще несколько сложных примеров. Ниже приведены три запроса, одинаковые по значению, которые с содержательной точки зрения определяют, какие студенты посещают оба указанных курса, каждый из которых использует соответственно соединение, подзапросы с предикатом IN и подзапросы с предикатом

EXISTS.

126

SELECT DISTINCT S1.name, S1.class FROM

Student S1 JOIN Attends A1 ON A1.student = S1.name

JOIN Attends A2 ON A2.student = S1.name

(11.23)

WHERE A1.title = ‘OS’ AND A2.title = ‘DBMS’

SELECT name, class FROM Student

(11.24)

WHERE name in

 

(SELECT student FROM Attends WHERE title = ‘OS’ ) AND name in

(SELECT student FROM Attends WHERE title = ‘DBMS’)

SELECT S.name, S.class FROM Student S WHERE EXISTS

(SELECT * FROM Attends WHERE student = S.name AND title = ‘OS’ )

AND EXISTS

(SELECT * FROM Attends WHERE student = S.name

AND title = ‘DBMS’ )

(11.25)

 

 

 

 

 

NAME

CLASS

 

 

Jane

 

3

 

Рис. 11.11. Значение запроса 11.25

Во всех трех вариантах из таблицы Attends производятся две независимые выборки (для каждого курса в отдельности) – либо через повторное соединение, либо при помощи независимых подзапросов; в запросе 11.25 подзапросы коррелированные, поэтому этот вариант является менее предпочтительным.

11.2. Подзапросы после FROM

Подзапросы могут использоваться вместо имен таблиц в SELECT-запросах. При вычислении подобных запросов сначала вычисляется подзапрос, а затем значение подзапроса используется как исходная таблица при вычислении основного запроса. Рекомендуется в таком подзапросе явно указывать имена полей создаваемого результирующего множества, чтобы эти имена можно было далее использовать в выражениях основного запроса.

Следующий простейший пример формально иллюстрирует применение подобных подзапросов:

127

SELECT * FROM ( SELECT * FROM Staff );

(11.26)

Конечно, использование запроса 11.26 вряд ли можно считать целесообразным, поскольку этот запрос эквивалентен обычному запросу SELECT * FROM Staff и здесь приводится исключительно для иллюстрации синтаксиса этого способа использования подзапросов. Следующий запрос 11.27 является более содержательным и демонстрирует применение подзапросов для дополнительного структурирования текста, однако и этот запрос может быть переписан без использования подзапроса.

SELECT «Имя», «Должность», «Количество» FROM

( SELECT S.name «Имя», S.position «Должность», COUNT(*) «Количество»

FROM Staff S, Course C

WHERE C.staff = S.name GROUP BY S.name, S.position )

WHERE «Имя» = ‘Won Kim’ ;

(11.27)

 

 

 

 

 

 

Имя

Должность

Количество

 

 

Won Kim

Full Professor

2

 

Рис. 11.12. Значение запроса 11.27

Следует обратить внимание, что приводимый ниже запрос 11.28 нельзя реализовать без использования подзапросов, поскольку результирующее множество каждого подзапроса группируется отдельно, а затем с ними выполняется соединение. С содержательной точки зрения этот запрос формирует список преподавателей и посещающих их курсы студентов, причем для каждого студента и преподавателя вычисляется количество посещаемых или читаемых ими всех курсов соответственно.

SELECT SC.«Имя», SC.«Должность», SC.«Кол.курсов», AC.«Студент», AC.«Кол.курсов» FROM

( SELECT S.name «Имя», S.position «Должность», COUNT(*) «Кол.курсов»

FROM Staff S JOIN Course C

ON C.staff = S.name

GROUP BY S.name, S.position ) SC JOIN

128

(SELECT A.student «Студент»,C.staff «Преподаватель»,

COUNT(*) «Кол.курсов»

 

 

 

 

FROM Attends A JOIN Course C

 

 

ON A.title = C.title

 

 

 

GROUP BY A.student, C.staff

) AC

(11.28)

ON SC.«Имя» = AC.«Преподаватель»;

 

 

 

 

 

 

 

 

 

Имя

Должность

 

Кол.

 

Студент

Кол.

 

курсов

 

курсов

 

 

 

 

 

G.Anderson

Senior Lector

 

3

 

Polie

1

G.Anderson

Senior Lector

 

3

 

Jane

2

G.Anderson

Senior Lector

 

3

 

Bill

1

Won Kim

Full Professor

 

2

 

Bill

1

 

 

 

 

 

 

 

Рис. 11.13. Значение запроса 11.28

Выводы

SQL-запросы могут содержать вложенные SELECTвыражения, называемые подзапросами;

подзапросы используются в логических выражениях в конструкциях WHERE и HAVING и в качестве «динамических» таблиц в конструкции FROM;

для многих (но не для всех) запросов с подзапросами можно написать эквивалентный запрос с соединением, и наоборот (но так же не для всех); использование подзапросов способствует повышению наглядности текста запросов;

подзапросы, использующие данные записей таблиц основного запроса, называются коррелированными; коррелированные запросы вычисляются для каждой записи таблицы основного запроса;

подзапросы, не использующие данные записей таблиц основного запроса, называются некоррелированными; коррелированные запросы вычисляются один раз для всех записей таблицы основного запроса;

подзапросы могут использоваться в операциях сравнения, в этом случае подзапрос должен возвращать только одну запись с одним значением;

129

подзапросы могут использоваться в предикате IN вместо списка значений;

подзапросы могут использоваться в предикате EXISTS, который в качестве параметра получает подзапрос и возвращает значение TRUE, если результирующее множество подзапроса содержит хотя бы одну запись;

подзапросы могут также использоваться вместо имен таблиц в SELECT-запросах; при вычислении подобных запросов сначала вычисляется подзапрос, а затем значение подзапроса используется как исходная таблица при вычислении основного запроса;

множественные операции UNION (объединение), UNION ALL (объединение с сохранением дубликатов), INTERSECT (пересечение) и MINUS (разность) используются для слияния результирующих множеств результирующих множеств двух SELECT-запросов.

Вопросы для контроля

1.Опишите основные виды подзапросов, используемых

вSELECT-запросах.

2.Объясните различия между корректированными и некоррелированными подзапросами.

3.Перечислите ограничения на структуру результирующего множества подзапросов, используемых в операциях сравнения.

4.Опишите отличия между подзапросами, используемыми в предикатах IN и EXISTS.

5.Какие: коррелированные или некоррелированные – подзапросы потенциально являются более эффективными с точки зрения исполнения?

6.Объясните смысл множественных операций.

130