model
Qty_model
Avg_price
1121
3
850.0
1232
4
425.0
1233
3
843.33333333333337
1260
1
350.0
Если бы в SELECT присутствовал столбец с датой, то можно было бы вычислять эти показатели для каждой конкретной даты. Для этого нужно добавить дату в качестве группирующего столбца, и тогда агрегатные функции вычислялись бы для каждой комбинации значений (модель−дата).
Существует несколько определенных правил выполнения агрегатных функций:
* Если в результате выполнения запроса не получено ни одной строки (или не одной строки для данной группы), то исходные данные для вычисления любой из агрегатных функций отсутствуют. В этом случае результатом выполнения функций COUNT будет нуль, а результатом всех других функций - NULL.
* Аргумент агрегатной функции не может сам содержать агрегатные функции (функция от функции). Т.е. в одном запросе нельзя, скажем, получить максимум средних значений.
* Результат выполнения функции COUNT есть целое число (INTEGER). Другие агрегатные функции наследуют типы данных обрабатываемых значений.
* Если при выполнении функции SUM был получен результат, превышающий максимальное значение используемого типа данных, возникает ошибка.
Итак, если запрос не содержит предложения GROUP BY, то агрегатные функции, включенные в предложение SELECT, исполняются над всеми результирующими строками запроса. Если запрос содержит предложение GROUP BY, каждый набор строк, который имеет одинаковые значения столбца или группы столбцов, заданных в предложении GROUP BY, составляет группу, и агрегатные функции выполняются для каждой группы отдельно.
Фильтрация данных по итоговым показателям. Предложение HAVING
Если предложение WHERE определяет предикат для фильтрации строк, то предложение HAVING применяется после группировки для определения аналогичного предиката, фильтрующего группы по значениям агрегатных функций. Это предложение необходимо для проверки значений, которые получены с помощью агрегатной функции не из отдельных строк источника записей, определенного в предложении FROM, а из групп таких строк. Поэтому такая проверка не может содержаться в предложении WHERE.
Пример. Получить количество ПК и среднюю цену для каждой модели при условии, что средняя цена менее $800:
SELECT model, COUNT(model) AS Qty_model, AVG(price) AS Avg_price
FROM PC
GROUP BY model
HAVING AVG(price) < 800;
В результате выполнения запроса получим:
model
Qty_model
Avg_price
1232
4
425.0
1260
1
350.0
Заметим, что в предложении HAVING нельзя использовать псевдоним (Avg_price), используемый для именования значений агрегатной функции. Дело в том, что предложение SELECT, формирующее выходной набор запроса, выполняется предпоследним перед предложением ORDER BY.
Ниже приведен порядок обработки предложений в операторе SELECT:
* FROM
* WHERE
* GROUP BY
* HAVING
* SELECT
* ORDER BY
Этот порядок не соответствует синтаксическому порядку общего формата оператора SELECT, представленному ниже:
SELECT [DISTINCT | ALL]{*
| [ [[AS] ]] [,…]}
FROM [[AS] ] [,…]
[WHERE ]
[[GROUP BY ]
[HAVING ] ]
[ORDER BY ]
Использование в запросе нескольких таблиц. Предложение FROM
Как видно из приведенного в конце предыдущего раздела синтаксиса оператора SELECT, в предложении FROM допускается указание нескольких таблиц. Простое перечисление таблиц практически не используется, поскольку оно соответствует реляционной операции декартова произведения. Т.е. в результирующем наборе каждая запись из одной таблицы будет сочетаться с каждой записью в другой. Например, для таблиц
A
B
a
b
c
d
1
2
2
4
2
1
3
3
Результат запроса
SELECT * FROM A, B;
будет выглядеть следующим образом:
a
b
c
d
1
2
2
4
1
2
3
3
2
1
2
4
2
1
3
3
Поэтому перечисление таблиц, как правило, используется совместно с условием соединения записей из разных таблиц, указываемым в предложении WHERE. Для приведенных выше таблиц таким условием может быть совпадение значений, скажем, в полях a и c:
SELECT * FROM A, B WHERE a=c;
Теперь результатом выполнения этого запроса будет следующая таблица:
a
b
c
d
2
1
2
4
т.е. соединяются только те строки таблиц, у которых в указанных полях находятся равные значения (эквисоединение). Естественно, могут быть использованы любые условия, хотя эквисоединение используется чаще всего, поскольку эта операция воссоздает некую сущность, декомпозированную на две других в результате процедуры нормализации.
Если разные таблицы имеют столбцы с одинаковыми именами, то для однозначности требуется использовать точечную нотацию:
.
В тех случаях, когда это не вызывает неоднозначности, использование данной нотации не является обязательным.
Пример. Найти номер модели и производителя ПК, имеющих цену менее $600:
SELECT DISTINCT PC.model, maker
FROM PC, Product
WHERE PC.model = Product.model AND price < 600;
В результате каждая модель одного и того же производителя выводится только один раз:
model
maker
1232
A
1260
E
Иногда в предложении FROM требуется указать одну и ту же таблицу несколько раз. В этом случае обязательным является переименование.
Пример. Вывести пары моделей, имеющих одинаковые цены:
SELECT DISTINCT A.model AS model_1, B.model AS model_2
FROM PC AS A, PC B
WHERE A.price = B.price AND A.model < B.model;
Здесь условие A.model < B.model используется для того, чтобы не выводились одинаковые пары, отличающиеся только перестановкой, например: 1232, 1233 и 1233, 1232. DISTINCT применяется для того, чтобы исключить одинаковые строки, поскольку в таблице PC имеются одинаковые модели по одной и той же цене. В результате получим следующую таблицу:
model_1
model_2
1232
1233
1232
1260
Переименование также требуется, если в предложении FROM используется подзапрос. Так, первый пример можно переписать следующим образом:
SELECT DISTINCT PC.model, maker
FROM PC,
(SELECT maker, model
FROM Product) AS prod
WHERE PC.model = prod.model AND price < 600;
Обратите внимание, что в этом случае в других предложениях оператора SELECT уже нельзя использовать квалификатор Product, поскольку таблица Product уже не используется. Вместо него используется псевдоним prod. Кроме того, ссылаться теперь можно только на те поля таблицы Product, которые перечислены в подзапросе.
Операции соединения. Предложения JOIN
В предложении FROM может быть указана явная операция соединения двух и более таблиц. Среди ряда операций соединения, описанных в стандарте языка SQL, многими серверами баз данных поддерживается лишь операция соединения по предикату. Синтаксис соединения по предикату имеет вид:
FROM [INNER]
| {{LEFT | RIGHT | FULL } [OUTER]} JOIN
[ON ]
Соединение может быть либо внутренним (INNER), либо одним из внешних (OUTER). Служебные слова INNER и OUTER можно опускать, поскольку внешнее соединение однозначно определяется его типом - LEFT (левое), RIGHT (правое) или FULL (полное), а просто JOIN будет означать внутреннее соединение.