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

Базы Данных - Сибилев, 2007

.pdf
Скачиваний:
290
Добавлен:
11.05.2015
Размер:
1.93 Mб
Скачать

191

FROM J JOIN SPJ

WHERE Snum = ‘S1’;

Вариант 2

SELECT DISTINCT Jnam

FROM J JOIN SPJ ON Snum = ‘S1

AND J.Jnum = SPJ.Jnum;

Вариант 3

SELECT Jnam

FROM J, (

SELECT DISTINCT Jnum

FROM SPJ

WHERE Snum = ‘S1

) AS JS1

WHERE J.Jnum = JS1.Jnum;

Вариант 4

SELECT DISTINCT Jnam

FROM J, SPJ

WHERE Snum = ‘S1

AND J.Jnum = SPJ.Jnum;

Возможны и другие варианты формулировок (см. п. 7.2.12).

Любой правильно сформулированный оператор выборки извлечёт из нашей БД таблицу, эквивалентную следующей:

Jnam

дисплей

процессор

Однако логика вариантов исполнения запросов различна.

Вариант 1 выглядит наиболее естественно. Формула оператора вы-

борки в точности следует логике словесной формулировки запроса в стиле

192

РА. Предложение FROM построит в рабочем буфере F-таблицу – естест-

венное соединение таблиц J и SPJ:

Jnum

Jnam

Ci

Snum

Pnum

Qt

J1

процессор

Асино

S4

P4

130

J1

процессор

Асино

S7

P6

2800

J1

процессор

Асино

S8

P1

400

J2

клавиатура

Яя

S7

P3

1000

 

 

 

 

 

 

J2

клавиатура

Яя

S7

P8

2500

J2

клавиатура

Яя

S8

P9

150

J3

дисплей

Томск

S1

P1

100

J3

дисплей

Томск

S1

P2

1000

J4

принтер

Саяногорск

S2

P9

200

J5

процессор

Яя

S1

P2

200

 

 

 

 

 

 

J6

клавиатура

Томск

S8

P1

2200

J6

клавиатура

Томск

S8

P3

100

J6

клавиатура

Томск

S8

P7

200

J7

мышь

Лесото

S2

P3

500

J8

джойстик

Лондон

S3

P4

150

J8

джойстик

Лондон

S5

P3

400

J9

винчестер

Рио-де-Жанейро

S3

P9

600

 

 

 

 

 

 

J9

винчестер

Рио-де-Жанейро

S6

P5

2800

 

 

 

 

 

 

J9

винчестер

Рио-де-Жанейро

S6

P8

300

Предложение WHERE выполнит селекцию F-таблицы по условию

Snum = ‘S1и создаст W-таблицу:

Jnum

Jnam

Ci

Snum

Pnum

Qt

J3

дисплей

Томск

S1

P1

100

J3

дисплей

Томск

S1

P2

1000

J5

процессор

Яя

S1

P2

200

Наконец, предложение SELECT со спецификатором DISTINCT соз-

даст требуемую проекцию W-таблицы на столбец Jnam. Спецификатор

DISTINCT необходим, т.к. исполняющая система не удаляет дубликаты строк автоматически.

Вариант 2, возможно, представляется менее наглядным, чем первый.

Формула оператора выборки соответствует следующей фразе: «Выполнить проекцию на атрибут Jnam соединения отношений J и SPJ по условию

Snum = ‘S1’ AND J.Jnum = SPJ.Jnum». В этом случае F-таблица эквива-

лентна W-таблице предыдущего варианта и предложение WHERE в опе-

раторе выборки не нужно.

193

Вариант 3, по-видимому, наименее нагляден. Он описывает сле-

дующую процедуру:

создать отношение JS1 как проекцию на атрибут Jnum селекции отношения SPJ по условию Snum = ‘S1’;

выполнить проекцию на атрибут Jnam селекции расширенного прямого произведения отношений J и JS1 по условию J.Jnum = JS1.Jnum.

Реализующий первый шаг подзапрос в предложении FROM произ-

ведёт неименованную временную таблицу. Для ссылок внутри включаю-

щего оператора SELECT ей будет присвоен псевдоним JS1.

JS1

Jnum

J3

J5

В результате обработки предложения FROM на втором шаге будет произведена следующая F-таблица:

J.Jnum

Jnam

Ci

JS1.Jnum

J1

процессор

Асино

J3

J1

процессор

Асино

J5

J2

клавиатура

Яя

J3

J2

клавиатура

Яя

J5

J3

дисплей

Томск

J3

J3

дисплей

Томск

J5

J4

принтер

Саяногорск

J3

J4

принтер

Саяногорск

J5

J5

процессор

Яя

J3

J5

процессор

Яя

J5

J6

клавиатура

Томск

J3

J6

клавиатура

Томск

J5

J7

мышь

Лесото

J3

J7

мышь

Лесото

J5

J8

джойстик

Лондон

J3

J8

джойстик

Лондон

J5

J9

винчестер

Рио-де-Жанейро

J3

J9

винчестер

Рио-де-Жанейро

J5

Обрабатывая предложение WHERE, система удалит все строки F-

таблицы, в которых значения столбцов J.Jnum и JS1.Jnum различны. Будет получена следующая W-таблица:

J.Jnum

Jnam

Ci

JS1.Jnum

 

 

 

194

 

 

 

 

 

 

J3

дисплей

Томск

 

J3

J5

процессор

Яя

 

J5

Наконец, будет выполнена SQL-проекция этой таблицы на столбец

Jnam.

Вариант 4 в словесной формулировке таков: «Выполнить проекцию на атрибут Jnam селекции расширенного прямого произведения отноше-

ний J и SPJ по условию Snum = ‘S1’ AND J.Jnum = SPJ.Jnum». Он (единст-

венный из приведённых) соответствует стандарту SQL1. Мы не будем приводить здесь F- и W-таблицы, производимые в процессе обработки этого запроса. Читателю рекомендуется проделать это самостоятельно.

В заключение заметим, что на самом деле логика исполнения экви-

валентных операторов SELECT различна только в представлении авторов формулировок. Всякая реальная СУБД обязательно имеет подсистему оп-

тимизации запросов. Задачи этой подсистемы – приведение предложенной пользователем формулировки запроса к канонической форме и построение оптимального плана выполнения запроса. Поэтому эффективность самой процедуры выборки данных зависит не от варианта записи оператора

SELECT, а от того, насколько удачно сконструирован оптимизатор.

Выразительные возможности SQL в части формулирования запросов к данным мало отличаются от возможностей естественного языка. Форму-

лируя запрос, следует стремиться к ясности формулировки для человека,

нимало не заботясь о СУБД.

Пример 2. Получить имена всех поставщиков и наименования изде-

лий, для которых они поставляют детали.

Источником данных являются таблицы S, J и SPJ. Словесная форму-

лировка предполагает использование внешнего соединения. Один из воз-

можных планов выполнения запроса таков:

–получить естественное соединение отношений J и SPJ;

–получить левое внешнее соединение отношения S и результата пре-

дыдущего шага;

195

–выполнить проекцию результата предыдущего шага на атрибуты

Snam и Jnam.

Вот соответствующая этому плану формула оператора выборки:

SELECT DISTINCT Snam, Jnam

FROM S LEFT JOIN (J JOIN SPJ);

а вот результат его исполнения:

Snam

Jnam

Владимир

клавиатура

 

 

Владимир

процессор

 

 

Григорий

винчестер

 

 

Григорий

джойстик

 

 

Егор

NULL

Иван

дисплей

Иван

процессор

 

 

Константин

джойстик

 

 

Николай

мышь

 

 

Николай

принтер

Петр

процессор

 

 

Иван

винчестер

 

 

Сергей

клавиатура

 

 

Сергей

процессор

Эквивалентный результат произведёт оператор

SELECT DISTINCT Snam, Jnam

FROM (S LEFT JOIN SPJ) JOIN J;

Читатель, без сомнения, сможет предложить ещё ряд эквивалентных формул SQL2. Однако SQL1 не имеет столь компактных средств записи внешних соединений. Формула SQL1 для рассматриваемого запроса при-

ведена в п. 7.2.15

7.2.10 Предложение GROUP BY и агрегатные функции SQL

Оператор SELECT, включающий предложение GROUP BY, реали-

зует реляционную операцию подведения итогов [Ошибка! Источник ссылки не найден., п. 2.5.5]. Предложение GROUP BY определяет груп-

пы строк рабочего буфера (F- или W-таблицы), для которых должны быть вычислены значения агрегатных функций. После обработки предложения

196

строки рабочего буфера оказываются собранными в группы с одинаковы-

ми значениями столбцов, указанных после ключевого слова.

Например, если рабочий буфер содержит эквивалент таблицы SPJ, и

система приняла предложение

GROUP BY Snum, Jnum

то после его обработки в буфере будет содержаться следующая G-

таблица21:

Snum

Pnum

Jnum

Qt

S1

P1

J3

100

S1

P2

J3

1000

 

 

 

 

S2

P9

J4

200

S1

P2

J5

200

S7

P6

J1

2800

 

 

 

 

S3

P9

J9

600

S5

P3

J8

400

S6

P8

J9

300

S6

P5

J9

2800

 

 

 

 

S7

P8

J2

1000

S7

P3

J2

2500

 

 

 

 

S8

P7

J6

400

S8

P1

J6

150

S8

P3

J6

100

S8

P9

J2

200

S8

P1

J1

2200

 

 

 

 

S4

P4

J1

130

S2

P3

J7

500

S3

P4

J8

150

Замечание. При обработке все NULL-значения солбцов группирова-

ния считаются одинаковыми.

Для каждой группы строк, определённой предложением GROUP BY,

создаётся одна строка выходной таблицы запроса. Поэтому элементом це-

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

GROUP BY, либо агрегатная функция, либо выражение, содержащее ссылки на столбцы группирования и агрегатные функции и принимающее единственное значение для группы строк.

21 Отдельные группы разделены горизонтальными линиями. Заметьте, что группы не упорядочены.

197

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

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

Он предполагает наличие средств объявления функциональных зависимо-

стей. Стандарты же не поддерживают как обязательное даже требование целостности сущности.

Стандарты SQL дают следующее определение синтаксиса агрегат-

ных функций:

агрегатная_функция ::= { COUNT(*) } |

{

{ COUNT | SUM | AVG | MAX | MIN }

( [DISTINCT | ALL] выражение )

}

Здесь выражение не может содержать агрегатные функции или подзапросы. На практике это, как правило, имя столбца источника данных. Однако стандарт допускает и более сложные выражения.

Если оператор выборки содержит предложение GROUP BY, то агрегатные функции могут использоваться в его целевом списке и в предикате предложения HAVING. При этом их значения вычисляются на

группах строк – по одному для каждой группы. В противном случае агрегатные функции можно использовать только в целевом списке и их значения вычисляются на всём множестве строк источника данных. Ни-

когда агрегатная функция не может встретиться в предикатах пред-

ложений WHERE или FROM иначе, как в подзапросе.

Функция COUNT(*) возвращает полное число строк в группе, вклю-

чая пустые и дубликаты.

При вычислении значений остальных пяти функций действуют сле-

дующие правила:

− перед вычислением значения функции все неопределённые значе-

198

ния аргумента исключаются;

− если определён параметр DISTINCT, то дубликаты значений аргу-

мента в подсчёте значения функции не участвуют, в противном случае ис-

пользуются все значения аргумента (параметр ALL определён по умолча-

нию);

− если аргумент пуст, т.е. содержит только NULL-значения, то функ-

ция COUNT возвращает значение 0 (ноль), а все остальные функции – значение NULL;

функция COUNT всегда возвращает целое число; другие функции наследуют типы аргументов;

функци SUM и AVG работают только с аргументами числовых ти-

пов;

− функции COUNT, MAX и MIN могут использоваться с аргумен-

тами любых типов.

Пример 1. Получить общее число зарегистрированных поставок.

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

SELECT COUNT(*) AS Число_поставок

FROM SPJ;

Запрос возвратит следующую таблицу:

Число_поставок

19

В нашем частном случае это истина. Однако таблицы SQL – это не отношения. Представим себе, что не определён первичный ключ таблицы

SPJ. Тогда в ней могут оказаться и частично или полностью пустые строки,

и дубликаты строк, т.е. мусор, который нередко встречается в БД, создан-

ных дилетантами. Наш оператор подсчитает все строки. Поэтому, если мы не уверены в том, что таблица не содержит мусора, нам следует сформули-

ровать запрос так: «Подсчитать число строк таблицы SPJ, в которых все столбцы имеют определённые значения и наборы значений столбцов

199

Snum, Pnum, Jnum не повторяются». Попытайтесь самостоятельно записать соответствующий оператор. Он будет значительно сложнее приведённого выше.

Пример 2. Подсчитать средний объём поставки детали P1.

SELECT AVG(Qt)

FROM SPJ

WHERE Pnum = ‘P1’;

900

Пример 3. Для каждого поставщика подсчитать количество постав-

ляемых им видов деталей.

Это двусмысленная формулировка, но, скорее всего, именно так и будет сформулировано пожелание бухгалтера, для которого нужно создать программу подготовки отчётов. Что ему нужно на самом деле? Количество видов деталей, поставляемых каждым поставщиком, зарегистрированным в БД? Или только тем, кто реально выполнял поставки? Какие сведения о поставщике должны быть включены в выходную таблицу? Только номер?

Номер и имя? И т.п.

Пусть после уточнения требования пользователя оказались следую-

щими: «Для каждого существующего поставщика получить его номер и количество поставляемых им видов деталей». В текущем состоянии БД с некоторыми поставщиками могут быть не связаны какие-либо поставки.

Поэтому в качестве источника данных следует использовать левое внеш-

нее соединение таблиц S и SPJ.

SELECT S.Snum AS Поставщик,

COUNT(DISTINCT Pnum) AS Видов

FROM S LEFT JOIN SPJ

GROUP BY S.Snum;

Поставщик

Видов

 

 

S1

2

S7

3

200

S3

2

S9

0

S5

1

S6

2

S4

1

S8

4

S2

2

Замечание. В целевой список запроса не может быть включён, на-

пример, столбец Snam, так как он не входит в список столбцов группиро-

вания.

7.2.11 Предложение HAVING

Предложение HAVING определяет условие, которому должны удов-

летворять группы строк, созданные предложением GROUP BY. Предикат предложения HAVING может содержать только выражения для вычисле-

ния значений, принимающие единственное значение на группе строк.

Обычно используются агрегатные функции и ссылки на столбцы группи-

рования. В выходную таблицу запроса попадают только строки, соответст-

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

HAVING используется без GROUP BY, то группой строк, на которой оце-

нивается его предикат, является всё множество строк таблицы предыду-

щего уровня.

Пример 1. Получить значения номеров поставщиков, номеров дета-

лей и превосходящих 500 штук суммарных объёмов поставок этих деталей,

выполненных этими поставщиками.

SELECT Snum AS Поставщик,

Pnum AS Деталь,

SUM(Qt) AS Объём

FROM SPJ

GROUP BY Snum, Pnum

HAVING SUM(Qt) > 500;

Этот оператор произведёт следующую таблицу:

Поставщик Деталь Объём