Читать книгу Основы программирования в СУБД Oracle. SQL+PL/SQL. - О. А. Ткачев - Страница 22
ЧАСТЬ 2. SQL – ЯЗЫК СТРУКТУРИРОВАННЫХ ЗАПРОСОВ
Глава 4. Агрегатные функции и группировка данных
ОглавлениеАгрегатные функции
В отличие от однострочных функций, агрегатные функции обрабатывают группу строк и возвращают один результат для группы. Группа строк может включать как всю таблицу, так и ее часть.
Таблица 4.1. Агрегатные функции
Синтаксис агрегатных функций:
{имя функции} ({Аргумент})
где: expr – аргумент агрегатной функции, который может содержать следующие элементы:
[DISTINCT] {имя столбца} | {выражение} | {однострочная функция}
Следует обратить внимание на то, что аргументом групповой функции может быть однострочная функция. Хотя стандарт языка SQL запрещает использование агрегатных функций в качестве аргумента агрегатных функций, СУБД Oracle допускает это, но только на один уровень в глубину и только в предложении SELECT. Рассмотрим примеры использования агрегатных функций.
Пример 4.1. Вывод обобщенных данных о зарплате сотрудников
SELECT MIN (salary) AS minimum, MAX (salary) AS maximum, ROUND (AVG (salary)) AS medium, SUM (salary) As summa, COUNT (salary), COUNT (*)
FROM Employees;
В полученном результате следует обратить внимание на то, что:
– COUNT (salary) возвращает число сотрудников, получающих зарплату, у которых значение столбца salary не NULL;
– COUNT (*) возвращает число всех сотрудников.
Этот запрос не учитывает то, что некоторые сотрудники получают комиссионные. Зарплата сотрудника с учетом комиссионных может быть вычислена путем использования выражения:
salary * (1 + NVL (commission_pct,0))
Используя это выражение в предыдущем запросе, вместо столбца salary получим:
Пример 4.2. Вывод обобщенных данных о зарплате сотрудников с учетом комиссионных
SELECT MIN (salary* (1+NVL (commission_pct,0))) AS minimum,
MAX (salary* (1+NVL (commission_pct,0))) AS maximum,
ROUND (AVG (salary* (1+NVL (commission_pct,0)))) AS medium,
SUM (salary* (1+NVL (commission_pct,0))) As summa,
COUNT (salary* (1+NVL (commission_pct,0))) AS ′′COUNT (expr) ′′,
COUNT (*)
FROM Employees;
Пример 4.3. Использование функции COUNT
SELECT COUNT (*), COUNT (salary),COUNT (DISTINCT salary),
COUNT (commission_pct)
FROM Employees
WHERE department_id =80;
Анализ результатов этого запроса:
– COUNT (*) – вернула число сотрудников в отделе 80;
– COUNT (salary) – вернула число сотрудников в отделе 80,
у которых значение столбца salary не NULL;
– COUNT (DISTINCT salary) – вернула число различных значений в столбце salary;
– COUNT (commission_pct) – вернула число сотрудников в отделе 80, у которых значение столбца commission_pct не NULL.
Оператор DISTINCT используется для исключения повторяющихся значений. Например, необходимо определить количество должностей. Запрос без оператора DISTINCT вернет количество сотрудников, у которых значение столбца job_id не NULL.
Пример 4.4. Количество сотрудников, у которых значение столбца job_id не NULL
SELECT COUNT (job_id)
FROM Employees;
Если в аргумент функции COUNT добавить оператор DISTINCT, то будут исключены повторяющиеся значения столбца job_id и запрос вернет количество должностей (количество уникальных значений столбца job_id).
Пример 4.5. Количество уникальных значений столбца job_id
SELECT COUNT (DISTINCT job_id)
FROM Employees;
Задача: требуется определить средний размер комиссионных. Рассмотрим два варианта решения этой задачи.
Вариант 1
SELECT AVG (commission_pct)
FROM Employees;
Вариант 2.
SELECT AVG (NVL (commission_pct, 0))
FROM Employees;
Здесь правильный вариант решения не очевиден. У значительной части сотрудников значение столбца commission_pct имеет значение NULL. Если этим сотрудникам комиссионные не положены и они не должны учитываться, то правильным будет первый вариант. Если значение NULL следует считать равным нулю, то следует использовать второй вариант запроса.
Агрегатные функции нельзя использовать в предложении WHERE. Например, нельзя найти сотрудника с максимальной зарплатой, используя следующий запрос:
Пример 4.6a. Найти сотрудника, получающего максимальную зарплату
Внимание: ЭТОТ ЗАПРОС НЕ БУДЕТ ВЫПОЛНЕН!
SELECT employee_id, salary
FROM Employees
WHERE salary = MAX (salary);
Данную задачу можно решить следующим образом:
Пример 4.6б. Найти сотрудника, получающего максимальную зарплату
SELECT employee_id, salary AS maximum
FROM Employees
WHERE salary = (SELECT MAX (salary) FROM Employees);
Данный запрос содержит в предложении WHERE подзапрос. Использование подзапросов будет рассмотрено позже.
Группировка
Чаще всего агрегатные функции используются в запросах с группировкой. В общем виде запрос с группировкой может быть представлен в следующем виде:
SELECT {список столбцов*), {агрегатные функции}
FROM {таблица}
WHERE {условия}
GROUP BY {список столбцов*}
HAVING {условия на группу};
Списки столбцов в предложениях SELECT и GROUP BY должны совпадать.
Предложение GROUP BY разбивает данные на группы, и запрос выводит обобщенные данные о каждой группе.
Рассмотрим примеры задач, для решения которых необходимо использовать группировку и агрегатные функции.
Пример 4.7. Для каждого отдела определить суммарную зарплату
SELECT department_id, SUM (salary) AS SUM_salary
FROM Employees
GROUP BY department_id
ORDER BY department_id;
Пример 4.8. Для каждого отдела определить суммарную зарплату с учетом комиссионных
SELECT department_id, SUM (salary* (1+NVL (commission_pct,0)))
As sum_sal
FROM Employees
GROUP BY department_id
ORDER BY department_id;
Пример 4.9. Для каждого отдела определить суммарную длину имен (столбца first_name)
SELECT department_id, SUM (LENGTH (first_name)) As sum_f_nam
FROM Employees
GROUP BY department_id
ORDER BY department_id;
Группировка по нескольким столбцам
В предложении GROUP BY можно указать несколько столбцов. В этом случае группу образуют строки с совпадающими значениями всех столбцов, по которым осуществляется группировка. Рассмотрим задачи, в которых требуется группировка по нескольким столбцам.
Сначала рассмотрим запрос, который содержит типичную ошибку при решении задач, требующих группировки по нескольким столбцам.
Пример 4.10а. Для каждого отдела определить должности и количество сотрудников, занимающих эту должность (содержит ошибку)
SELECT department_id, job_id, count (*)
FROM employees
GROUP BY department_id;
Причина ошибки: при наличии группировки предложение SELECT может содержать только столбцы, по которым осуществляется группировка и агрегатные функции.
Пример 4.10б. Для отделов 30 и 50 определить должности и количество сотрудников, занимающих каждую должность
SELECT department_id, job_id, count (*)
FROM Employees
WHERE department_id IN (30,50)
GROUP BY department_id, job_id
ORDER BY department_id;
Пример 4.11. Для отделов, номер которых меньше 50, вывести рейтинги, которые имеют сотрудники этого отдела, их количество и суммарную зарплату
SELECT department_id, rating_e, count (*),sum (salary)
FROM Employees
WHERE department_id <= 50
GROUP BY department_id, rating_e
ORDER BY department_id;
Использование условий на группу
Условия на группу указываются в предложении HAVING.
Пример 4.12. Вывести суммарную зарплату для тех отделов, у которых суммарная зарплата превышает 50 000
SELECT department_id, SUM (salary)
FROM Employees
GROUP BY department_id
HAVING SUM (salary)> 50000;
Условие выборки может быть проверено до группировки. В этом случае сначала выбираются строки, удовлетворяющие условию, а потом осуществляется группировка полученных данных. Для полученных групп можно указать условие в предложении HAVING. В результат запроса попадут только те группы, которые удовлетворяют этому условию.
Пример 4.13. Вывести должности и количество сотрудников, которые получают зарплату более 10 000
SELECT department_id, SUM (salary)
FROM Employees
GROUP BY department_id
HAVING SUM (salary)> 50000;
К условиям предыдущей задачи можно добавить условие: вывести только те должности, которые занимают более одного сотрудника.
Пример 4.14. Вывести должности и количество сотрудников, которые получают зарплату более 10 000, которые занимают более одного сотрудника, упорядочив их в порядке убывания количества сотрудников
SELECT job_id, COUNT (*) As num_job
FROM Employees
WHERE salary> 10000
GROUP BY job_id
HAVING COUNT (*)> 1
ORDER BY num_job DESC;
Пример 4.15. Вывести номера отделов, у которых число сотрудников, имеющих рейтинг 5, больше одного, количество сотрудников, имеющих рейтинг 5, и их суммарную зарплату
SELECT department_id, rating_e, count (*),sum (salary)
FROM Employees
WHERE rating_e = 5
GROUP BY department_id, rating_e
HAVING count (*)> 1
ORDER BY department_id;
Использование вложенных агрегатных функций
Хотя Oracle и допускает использование вложенных агрегатных функций, но только на один уровень и только в предложении SELECT. При этом предложение SELECT не должно содержать других элементов.
Пример 4.16. Определить количество сотрудников в каждом отделе
SELECT department_id, count (*)
FROM Employees
GROUP BY department_id
ORDER BY department_id;
Используя вложенные агрегатные функции, можно найти максимальное число сотрудников, работающих в одном отделе.
Пример 4.17. Найти максимальное число сотрудников работающих в одном отделе
SELECT Max (COUNT (*))
FROM Employees
GROUP BY department_id;
Но вложенные агрегатные функции нельзя использовать в предложениях WHERE и HAVING, поэтому для того, чтобы найти отдел, в котором работает максимальное число сотрудников, понадобится использование подзапроса.
Пример 4.18. Найти отдел, в котором работает максимальное число сотрудников.
SELECT department_id, COUNT (*)
FROM Employees
GROUP BY department_id
HAVING COUNT (*) =
(SELECT MAX (COUNT (*))
FROM Employees
GROUP BY department_id);
Использование специальных операторов группировки
Рассмотрим специальные операторы группировки и функции, которые позволяют существенно расширить возможности запросов с группировкой данных.
Оператор GROUP BY ROLLUP
Расширяет возможности GROUP BY, возвращая для каждой группы строку, содержащую итоги по группе, а также строку, содержащую общий итог для всех групп, и имеет следующий вид:
GROUP BY ROLLUP {список столбцов}
Для демонстрации возможностей, которые предоставляет оператор GROUP BY ROLLUP, рассмотрим следующую задачу: для каждого отдела определить должности и количество сотрудников, занимающих эту должность. Решение этой задачи без использования ROLLUP содержится в примере 4.10.
Пример 4.19. Для отделов 30 и 50 определить должности и количество сотрудников, занимающих эту должность
SELECT department_id, job_id, count (*)
FROM Employees
WHERE department_id IN (30,50)
GROUP BY ROLLUP (department_id, job_id)
ORDER BY department_id;
По сравнению с результатами, которые выводит запрос из примера 4.10, этот запрос выводит данные о количестве сотрудников в каждом отделе и общем количестве сотрудников, работающих в рассматриваемых отделах.
В условия группировки можно добавить столбец rating_e.
Пример 4.20. Для отделов 30 и 50 определить должности, рейтинг и количество сотрудников, занимающих каждую должность и имеющих определенный рейтинг
SELECT department_id, job_id, rating_e, count (*)
FROM Employees
WHERE department_id IN (30,50)
GROUP BY ROLLUP (department_id, job_id, rating_e)
ORDER BY department_id;
Оператор GROUP BY CUBE
Возвращает предварительные итоги для всех комбинаций столбцов и строку с общим итогом и имеет следующий вид:
GROUP BY CUBE {список столбцов}
Рассмотрим решение предыдущих задач с использованием этого оператора.
Пример 4.21. Для отделов 30 и 50 определить должности и количество сотрудников, занимающих каждую должность
SELECT department_id, job_id, count (*)
FROM Employees
WHERE department_id IN (30,50)
GROUP BY CUBE (department_id, job_id)
ORDER BY department_id;
Сравнивая эти результаты с результатами из примера 4.19, можно увидеть, что результат последнего запроса содержит данные о количестве сотрудников, занимающих определенную должность, без учета отдела, в котором они работают.
В примере 4.22 приведен вариант решения задачи из примера 4.20 с использованием оператора GROUP BY CUBE.
Пример 4.22. Для отделов 30 и 50 определить должности, рейтинг и количество сотрудников, занимающих каждую должность и рейтинг и имеющих рейтинг> 3