Агрегатные функции (Transact-SQL)
Агрегатная функция выполняет вычисление на наборе значений и возвращает одиночное значение. Агрегатные функции, за исключением COUNT(*) , не учитывают значения NULL. Агрегатные функции часто используются в выражении GROUP BY инструкции SELECT.
Все агрегатные функции являются детерминированными. Другими словами, агрегатные функции возвращают одну и ту же величину при каждом их вызове на одном и том же наборе входных значений. Дополнительные сведения о детерминированности функций см. в статье Детерминированные и недетерминированные функции. Предложение OVER может следовать за всеми агрегатными функциями, кроме STRING_AGG, GROUPING или GROUPING_ID.
Агрегатные функции можно использовать в качестве выражений только в следующих случаях.
- Список выбора инструкции SELECT (вложенный или внешний запрос).
- Предложение HAVING.
Transact-SQL предоставляет следующие агрегатные функции:
Оператор SELECT
Наиболее используемым, но и самым сложным оператором является оператор выборки SELECT. Он позволяет производить выборку данных из таблиц и преобразовывать к нужному виду полученные результаты.
20 дек. 2020 · 9 минуты на чтение
Результатом выполнения оператора SELECT является таблица. К этой таблице может быть снова применен оператор SELECT и т.д., то есть такие операторы могут быть вложены друг в друга. Вложенные операторы SELECT называют подзапросами.
Синтаксис оператора SELECT использует следующие основные предложения:
SELECT FROM [WHERE ] [GROUP BY ] [HAVING ] [ORDER BY ]
Кратко пояснить смысл предложений оператора SELECT можно следующим образом:
- SELECT — выбрать данные из указанных столбцов и (если необходимо) выполнить перед выводом их преобразование в соответствии с указанными выражениями и (или) функциями
- FROM — из перечисленных таблиц, в которых расположены эти столбцы
- WHERE — где строки из указанных таблиц должны удовлетворять указанному перечню условий отбора строк
- GROUP BY — группируя по указанному перечню столбцов с тем, чтобы получить для каждой группы единственное значение
- HAVING — имея в результате лишь те группы, которые удовлетворяют указанному перечню условий отбора групп
- ORDER BY — сортируя по указанному перечню столбцов
Как видно из синтаксиса рассматриваемого оператора, обязательными являются только два первых предложения: SELECT и FROM .
Рассмотрим каждое предложение оператора SELECT .
Спонсор поста
База данных для примеров
Дальше будет много примеров и логично постоянно использовать одну и ту же БД. Так что на основании базы данных ниже будут продемонстрированы все примеры, не только в этой статье, но и в других.
Постановка задачи: пусть требуется разработать БД для предметной области «Поставка деталей»!
Требуется хранить следующую информацию:

- О поставщиках (P) pnum, pname
- О деталях (D) pnum, dname, dprice
- О поставках (PD) volume
Значения таблицы P
| pnum | pname |
|---|---|
| 1 | Иванов |
| 2 | Петров |
| 3 | Сидоров |
| 4 | Кузнецов |
Значения таблицы D
| pnum | dname | dprice |
|---|---|---|
| 1 | Болт | 10 |
| 2 | Гайка | 20 |
| 3 | Винт | 30 |
Значения таблицы PD
| pnum | dnum | volume |
|---|---|---|
| 1 | 1 | 100 |
| 1 | 2 | 100 |
| 1 | 3 | 300 |
| 2 | 1 | 150 |
| 1 | 2 | 250 |
| 3 | 1 | 1000 |
Предложение SELECT
После служебного слова SELECT перечисляются имена столбцов, значения которых будут входить в результат выполнения запроса.
Столбцы в результирующей таблице размещаются в том порядке, в котором они были указаны в предложении SELECT . Имена столбцов указываются через запятую.
Если имя столбца содержит пробелы или разделители, то его необходимо заключить в квадратные скобки.
При обработке данных из разных таблиц может возникнуть ситуация, когда столбцы разных таблиц имеют одинаковые имена. В этом случае имя столбца необходимо записывать как составное, указывая перед ним имя соответствующей таблицы: .
Предложение FROM
В предложении FROM перечисляются имена таблиц, которые содержат столбцы, указанные после слова SELECT .
Пример 1.
Вывести список наименований деталей из таблицы D (“Детали”).
SELECT dname FROM D
Пример 2.
Получить всю информацию из таблицы D (“Детали”).
Получить результат можно двумя способами:
-
Явным указанием всех столбцов таблицы.
SELECT dnum, dname, dprice FROM D
SELECT * FROM D
В результате и первого и второго запроса получаем новую таблицу, представляющую собой полную копию таблицы D (“Детали”).
Можно осуществить выбор отдельных столбцов и их перестановку.
Пример 3.
Получить информацию о наименовании и номере поставщика.
SELECT pname, pnum FROM P
Пример 4.
Определить номера поставщиков, которые поставляют детали в настоящее время (то есть номера тех поставщиков, которые присутствуют в таблице PD (“Поставки”)).
SELECT pnum FROM PD
| pnum |
|---|
| 1 |
| 1 |
| 1 |
| 2 |
| 2 |
| 3 |

Дополнительно о SELECT
Теперь, когда мы научились делать простые запросы с SELECT и FROM , можно ненадолго снова вернуться к SELECT .
Агрегатные функции
В операторе SELECT можно использовать агрегатные функции, которые дают единственное значение для целой группы строк в таблице.
Агрегатная функция записывается в следующем виде: ()
Пользователю доступны следующие агрегатные функции:
- SUM ‑ вычисляет сумму множества значений указанного столбца;
- COUNT ‑ вычисляет количество значений указанного столбца;
- MIN / MAX ‑ определяет минимальное/максимальное значение в указанном столбце;
- AVG ‑ вычисляет среднее арифметическое значение множества значений столбца;
- FIRST / LAST ‑ определяет первое/последнее значение в указанном столбце.
Пример 5.
Определить общий объем поставляемых деталей.
SELECT SUM(volume) FROM PD
| Expr1000 |
|---|
| 2000 |
Вычисляемые столбцы
Столбцы результирующей таблицы, которых не существовало в исходных таблицах, называются вычисляемыми. Таким столбцам СУБД присваивает системные имена, что не всегда является удобным.
При вычислении результатов любой агрегатной функции СУБД сначала исключает все NULL -значения, после чего требуемая операция применяется к оставшимся значениям.
Для функции COUNT возможен особый вариант использования — COUNT(*) . Его назначение состоит в подсчете всех строк в результирующей таблице, включая NULL -значения.
Следует запомнить, что агрегатные функции нельзя вкладывать друг в друга. Такая конструкция работать не будет: MAX(SUM(VOLUME))
Переименование столбца
Язык SQL позволяет задавать новые имена столбцам результирующей таблицы, для чего используется операция AS . Переименование также используют для изменения сложных имен столбцов таблицы.
Например, присвоить новое имя вычисляемому столбцу в предыдущем примере позволит выполнение следующего запроса.
SELECT SUM(volume) AS SUM FROM PD
Пример 6.
Определить количество поставщиков, которые поставляют детали в настоящее время.
SELECT COUNT(pnum) AS COUNT FROM PD
Несмотря на то, что реальное число поставщиков деталей в таблице PD равно 3, СУБД возвращает число 6. Такой результат объясняется тем, что СУБД подсчитывает все строки в таблице PD, не обращая внимание на то, что в строках есть одинаковые значения.
Операция DISTINCT
Если до применения агрегатной функции необходимо исключить дублирующиеся значения, следует перед именем столбца указать ключевое слово DISTINCT .
SELECT COUNT(DISTINCT pnum) AS COUNT FROM PD
DISTINCT можно задать только один раз для одного предложения SELECT.
Противоположностью DISTINCT является операция ALL . Она имеет противоположное действие «показать все строки таблицы» и предполагается по умолчанию.
Операция TOP
Итоговый набор записей, получаемых после выполнения запроса можно ограничить первыми N строками или первыми N процентами от общего количества строк результата.
Для этого используется операция TOP , которая записывается в предложении SELECT следующим образом: SELECT TOP N [PERCENT]
Пример 7.
Определить номера первых двух деталей таблицы D.
SELECT TOP 2 dnum FROM D
Стандарт SQL требует, чтобы при сортировке NULL -значения трактовались либо как превосходящие, либо как уступающие по сравнению со всеми остальными значениями. Так как конкретный вариант стандартом не оговаривается, то в зависимости от используемой СУБД при сортировке NULL -значения следуют до или после остальных значений. В MS SQL Server NULL -значения считаются уступающими по сравнению с остальными значениями.
Рандомный блок
Предложение WHERE
После служебного слова WHERE указываются условия выбора строк, помещаемых в результирующую таблицу. Существуют различные типы условий выбора:
- Сравнение значений атрибутов со скалярными выражениями, другими атрибутами или результатами вычисления выражений.
- Проверка значения на принадлежность множеству.
- Проверка значения на принадлежность диапазону.
- Проверка строкового значения на соответствие шаблону.
- Проверка на наличие null -значения.
Сравнение
В языке SQL используются традиционные операции сравнения = , <> , < , , >= .
В качестве условия в предложении WHERE можно использовать сложные логические выражения, использующие атрибуты таблиц, константы, скобки, операции AND , OR , отрицание NOT .
Пример 8.
Определить номера деталей, поставляемых поставщиком с номером 2.
SELECT dnum FROM PD WHERE pnum = 2
Пример 9.
Получить информацию о поставщиках Иванов и Петров.
SELECT * FROM P WHERE pname='Иванов' OR pname='Петров'
Строковые значения атрибутов заключаются в апострофы.
Проверка на принадлежность множеству
Операция IN проверяет, принадлежит ли значение атрибута заданному множеству.
Пример 10.
Получить информацию о поставщиках ‘Иванов’ и ‘Петров’.
SELECT * FROM P WHERE pname IN ('Иванов','Петров')
Пример 11.
Получить информацию о деталях с номерами 1 и 2.
SELECT * FROM D WHERE dnum IN (1, 2)
Проверка на принадлежность диапазону
Операция BETWEEN определяет минимальную и максимальную границу диапазона, в которое должно попадать значение атрибута. Обе границы считаются принадлежащими диапазону.
Пример 12.
Определить номера деталей, с ценой от 10 до 20 рублей.
SELECT dnum FROM D WHERE dprice BETWEEN 10 AND 20
Пример 13.
Вывести наименования поставщиков, начинающихся с букв от ‘К’ по ‘П’.
SELECT pname FROM P WHERE pname BETWEEN 'К' AND 'Р'
Сравнение символов
Буква Р в условии запроса объясняется тем, что строки сравниваются посимвольно. Для каждого символа при этом определяется код. Для нашего случая справедливо условие: П < Петров < Р
Проверка строкового значения на соответствие шаблону
Операция LIKE используется для поиска подстрок. Значения столбца, указываемого перед служебным словом LIKE сравниваются с задаваемым после него шаблоном. Форматы шаблонов различаются в конкретных СУБД.
Для СУБД MS SQL Server:
- Символ % заменяет любое количество любых символов.
- Символ _ заменяет один любой символ.
- [] ‑ вместо символа строки может быть подставлен один любой символ из множества возможных, указанных в ограничителях.
- [^] ‑ вместо символа строки может быть подставлен любой из символов кроме символов из множества, указанного в ограничителях.
Множество символов в квадратных скобках можно указывать через запятую, либо в виде диапазона.
Пример 14.
Вывести фамилии поставщиков, начинающихся с буквы И .
SELECT pname FROM P WHERE pname LIKE 'И%'
Пример 15.
Вывести фамилии поставщиков, начинающихся с букв от К по П .
SELECT pname FROM P WHERE dname LIKE '[К-П]%'
Проверка на наличие null -значения
Операции IS NULL и IS NOT NULL используются для сравнения значения атрибута со значением NULL .
Пример 16.
Определить наименования деталей, для которых не указана цена.
SELECT dname FROM D WHERE dprice IS NULL
Пример 17.
Определить номера поставщиков, для которых указано наименование.
SELECT pnum FROM P WHERE pname IS NOT NULL
Предложение GROUP BY
Использование GROUP BY позволяет разбивать таблицу на логические группы и применять агрегатные функции к каждой из этих групп. В результате получим единственное значение для каждой группы.
Обычно предложение GROUP BY применяют, если формулировка задачи содержит фразу «для каждого…», «каждому..» и т.п.
Пример 18.
Определить суммарный объем деталей, поставляемых каждым поставщиком.
SELECT pnum, SUM(VOLUME) AS SUM FROM PD GROUP BY pnum
| pnum | sum |
|---|---|
| 1 | 600 |
| 2 | 400 |
| 3 | 1000 |
Выполнение запроса можно описать следующим образом: СУБД разбивает таблицу PD на три группы, в каждую из групп помещаются строки с одинаковым значением номера поставщика. Затем к каждой из полученных групп применяется агрегатная функция SUM , что дает единственное итоговое значение для каждой группы.
Рассмотрим два похожих примера. В примере 19 определяется минимальный объем поставки каждого поставщика. В примере 20 определяется объем минимальной поставки среди всех поставщиков.
Пример 19:
SELECT pnum, MIN(VOLUME) AS MIN FROM PD GROUP BY pnum
Пример 20:
SELECT MIN(VOLUME) AS MIN FROM P
Результаты запросов представлены в следующей таблице:
| pnum | min | max |
|---|---|---|
| 1 | 100 | 100 |
| 2 | 150 | |
| 3 | 1000 |
Следует обратить внимание, что в первом примере мы можем вывести номера поставщиков, соответствующие объемам поставок, а во втором примере – не можем.
Все имена столбцов, перечисленные после ключевого слова SELECT должны присутствовать и в предложении GROUP BY , за исключением случая, когда имя столбца является аргументом агрегатной функции.
Однако в предложении GROUP BY могут быть указаны имена столбцов, не перечисленные в списке вывода после ключевого слова SELECT .
Если предложение GROUP BY расположено после предложения WHERE , то группы создаются из строк, выбранных после применения WHERE .
Пример 21.
Для каждой из деталей с номерами 1 и 2 определить количество поставщиков, которые их поставляют, а также суммарный объем поставок деталей.
SELECT dnum, COUNT(pnum) AS COUNT, SUM(volume) AS SUM FROM PD WHERE dnum=1 OR dnum=2 GROUP BY dnum
| dnum | COUNT | SUM |
|---|---|---|
| 1 | 3 | 1250 |
| 2 | 2 | 450 |
Чтобы организовать вложенные группировки, после GROUP BY следует указать несколько группирующих столбцов через запятую. В этом случае реальный подсчет данных будет происходить по той группе, которая указана последней.
Предложение HAVING
Предложение HAVING определяет критерий, согласно которому, определенные группы, сформированные с помощью предложения GROUP BY , исключаются из результирующей таблицы.
Выполнение предложения HAVING сходно с выполнением предложения WHERE . Но предложение WHERE исключает строки до того, как выполняется группировка, а предложение HAVING — после. Поэтому предложение HAVING может содержать агрегатные функции, а предложение WHERE — не может.
Пример 22.
Определить номера поставщиков, поставляющих в сумме более 500 деталей.
SELECT pnum, SUM(volume) AS SUM FROM PD GROUP BY pnum HAVING SUM(volume) > 500
| pnum | SUM |
|---|---|
| 1 | 600 |
| 3 | 1000 |
Пример 23.
Определить номера поставщиков, которые поставляют только одну деталь.
SELECT pnum, COUNT(dnum) AS COUNT FROM PD GROUP BY pnum HAVING COUNT(dnum) = 1
Предложение ORDER BY
При выполнении запроса СУБД возвращает строки в случайном порядке. Предложение ORDER BY позволяет упорядочить выходные данные запроса в соответствии со значениями одного или нескольких выбранных столбцов.
Можно задать возрастающий — ASC (от слова Ascend) или убывающий — DESC (от слова Descend) порядок сортировки. По умолчанию принят возрастающий порядок сортировки.
Пример 24.
Отсортировать таблицу PD в порядке возрастания номеров поставщиков, а строки с одинаковыми значениями pnum отсортировать в порядке убывания объема поставок.
SELECT pnum, volume, dnum FROM PD ORDER BY pnum ASC, volume DESC
| pnum | volume | dnum |
|---|---|---|
| 1 | 300 | 3 |
| 1 | 200 | 2 |
| 1 | 100 | 1 |
| 2 | 250 | 2 |
| 2 | 150 | 1 |
| 3 | 1000 | 1 |
Операцию TOP удобно применять после сортировки результирующего набора с помощью предложения ORDER BY .
Пример 25.
Определить номера первых двух деталей с наименьшей стоимостью.
SELECT TOP 2 dnum FROM D ORDER BY dprice ASC
Следует отметить, что если в таблице D будут две детали без указания цены, то именно их и отобразит предыдущий запрос. Поэтому при наличии NULL -значений их необходимо исключать с помощью предложения WHERE .
SELECT TOP 2 dnum FROM D WHERE dprice IS NOT NULL ORDER BY dprice ASC
Заключение
В статье было рассмотрен оператор выборки SELECT . Знание оператора SELECT является ключевым при написании любых SQL-запросов. Он позволяет производить выборку данных из таблиц и преобразовывать результаты в соответствии с нужными выражениями и функциями.
Результатом выполнения оператора SELECT является таблица, которую можно вложить в другой оператор SELECT в качестве подзапроса.
Синтаксис оператора SELECT содержит несколько предложений, из которых обязательными являются только SELECT и FROM . Остальные предложения, такие как WHERE , GROUP BY , HAVING и ORDER BY , могут использоваться по желанию для уточнения выборки данных.
Инструкция SELECT: расширенные возможности

Следующие подразделы описывают другие предложения оператора SELECT, которые могут быть использованы в запросах, а также агрегатные функции и наборы операторов. Напомню, к данному моменту мы рассмотрели использование предложения WHERE, а в этой статье мы рассмотрим предложения GROUP BY, ORDER BY и HAVING, и предоставим некоторые примеры использования этих предложений в сочетании с агрегатными функциями, которые поддерживаются в Transact-SQL.
Предложение GROUP BY
Предложение GROUP BY группирует выбранный набор строк для получения набора сводных строк по значениям одного или нескольких столбцов или выражений. Простой случай применения предложения GROUP BY показан в примере ниже:
USE SampleDb; SELECT Job FROM Works_On GROUP BY Job;
В этом примере происходит выборка и группирование должностей сотрудников. Результат выполнения этого запроса:

В примере выше предложение GROUP BY создает отдельную группу для всех возможных значений (включая значение NULL) столбца Job.
Использование столбцов в предложении GROUP BY должно отвечать определенным условиям. В частности, каждый столбец в списке выборки запроса также должен присутствовать в предложении GROUP BY. Это требование не распространяется на константы и столбцы, являющиеся частью агрегатной функции. (Агрегатные функции рассматриваются в следующем подразделе.) Это имеет смысл, т.к. только для столбцов в предложении GROUP BY гарантируется одно значение для каждой группы.
Таблицу можно сгруппировать по любой комбинации ее столбцов. В примере ниже демонстрируется группирование строк таблицы Works_on по двум столбцам:
USE SampleDb; SELECT ProjectNumber, Job FROM Works_On GROUP BY ProjectNumber, Job;
Результат выполнения этого запроса:

По результатам выполнения запроса можно видеть, что существует девять групп с разными комбинациями номера проекта и должности. Последовательность имен столбцов в предложении GROUP BY не обязательно должна быть такой же, как и в списке столбцов выборки SELECT.
Агрегатные функции
Агрегатные функции используются для получения суммарных значений. Все агрегатные функции можно разделить на следующие категории:
- обычные агрегатные функции;
- статистические агрегатные функции;
- агрегатные функции, определяемые пользователем;
- аналитические агрегатные функции.
Здесь мы рассмотрим первые три типа агрегатных функций.
Обычные агрегатные функции
Язык Transact-SQL поддерживает следующие шесть агрегатных функций: MIN, MAX, SUM, AVG, COUNT, COUNT_BIG.
Все агрегатные функции выполняют вычисления над одним аргументом, который может быть или столбцом, или выражением. (Единственным исключением является вторая форма двух функций: COUNT и COUNT_BIG, а именно COUNT(*) и COUNT_BIG(*) соответственно.) Результатом вычислений любой агрегатной функции является константное значение, отображаемое в отдельном столбце результата.
Агрегатные функции указываются в списке столбцов инструкции SELECT, который также может содержать предложение GROUP BY. Если в инструкции SELECT отсутствует предложение GROUP BY, а список столбцов выборки содержит, по крайней мере, одну агрегатную функцию, тогда он не должен содержать простых столбцов (кроме как столбцов, служащих аргументами агрегатной функции). Поэтому код в примере ниже неправильный:
USE SampleDb; SELECT LastName, MIN(Id) FROM Employee;
Здесь столбец LastName таблицы Employee не должен быть в списке выборки столбцов, поскольку он не является аргументом агрегатной функции. С другой стороны, список выборки столбцов может содержать имена столбцов, которые не являются аргументами агрегатной функции, если эти столбцы служат аргументами предложения GROUP BY.
Аргументу агрегатной функции может предшествовать одно из двух возможных ключевых слов:
ALL
Указывает, что вычисления выполняются над всеми значениями столбца. Это значение по умолчанию.
DISTINCT
Указывает, что для вычислений применяются только уникальные значения столбца.
Агрегатные функции MIN и MAX
Агрегатные функции MIN и MAX вычисляют наименьшее и наибольшее значение столбца соответственно. Если запрос содержит предложение WHERE, функции MIN и MAX возвращают наименьшее и наибольшее значение строк, отвечающих указанным условиям. В примере ниже показано использование агрегатной функции MIN:
USE SampleDb; -- Вернет 2581 SELECT MIN(Id) AS 'Минимальное значение Id' FROM Employee;
Возвращенный в примере выше результат не очень информативный. Например, неизвестна фамилия сотрудника, которому принадлежит этот номер. Но получить эту фамилию обычным способом невозможно, потому что, как упоминалось ранее, явно указать столбец LastName не разрешается. Для того чтобы вместе с наименьшим табельным номером сотрудника также получить и фамилию этого сотрудника, используется подзапрос. В примере ниже показано использование такого подзапроса, где вложенный запрос содержит инструкцию SELECT из предыдущего примера:
USE SampleDb; SELECT Id, LastName FROM Employee WHERE MIN(Id) FROM Employee);
Результат выполнения запроса:

Использование агрегатной функции MAX показано в примере ниже:
USE SampleDb; -- 29346 SELECT Id, LastName FROM Employee WHERE MAX(Id) FROM Employee);
В качестве аргумента функции MIN и MAX также могут принимать строки и даты. В случае строкового аргумента значения сравниваются, используя фактический порядок сортировки. Для всех аргументов временных данных типа «дата» наименьшим значением столбца будет наиболее ранняя дата, а наибольшим — наиболее поздняя.
С функциями MIN и MAX можно применять ключевое слово DISTINCT. Перед применением агрегатных функций MIN и MAX из столбцов их аргументов исключаются все значения NULL.
Агрегатная функция SUM
Агрегатная функция SUM вычисляет общую сумму значений столбца. Аргумент этой агрегатной функции всегда должен иметь числовой тип данных. Использование агрегатной функции SUM показано в примере ниже:
USE SampleDb; SELECT SUM (Budget) 'Суммарный бюджет' FROM Project;
В этом примере происходит вычисление общей суммы бюджетов всех проектов. Результат выполнения запроса:

В этом примере агрегатная функция группирует все значения бюджетов проектов и определяет их общую сумму. По этой причине запрос содержит неявную функцию группирования (как и все аналогичные запросы). Неявную функцию группирования из примера выше можно указать явно, как это показано в примере ниже:
USE SampleDb; SELECT SUM (Budget) 'Суммарный бюджет' FROM Project GROUP BY();
Рекомендуется использовать этот синтаксис в предложении GROUP BY, поскольку таким образом группирование определяется явно.
Использование параметра DISTINCT устраняет все повторяющиеся значения в столбце перед применением функции SUM. Аналогично удаляются все значения NULL перед применением этой агрегатной функции.
Агрегатная функция AVG
Агрегатная функция AVG возвращает среднее арифметическое значение для всех значений столбца. Аргумент этой агрегатной функции всегда должен иметь числовой тип данных. Перед применением функции AVG все значения NULL удаляются из ее аргумента.
Использование агрегатной функции AVG показано в примере ниже:
USE SampleDb; -- Вернет 133833 SELECT AVG (Budget) 'Средний бюджет на проект' FROM Project;
Здесь происходит вычисление среднего арифметического значения бюджета для всех бюджетов.
Агрегатные функции COUNT и COUNT_BIG
Агрегатная функция COUNT имеет две разные формы:
COUNT([DISTINCT] col_name) COUNT(*)
Первая форма функции подсчитывает количество значений в столбце col_name. Если в запросе используется ключевое слово DISTINCT, перед применением функции COUNT удаляются все повторяющиеся значения столбца. При подсчете количества значений столбца эта форма функции COUNT не принимает во внимание значения NULL.
Использование первой формы агрегатной функции COUNT показано в примере ниже:
USE SampleDb; SELECT ProjectNumber, COUNT(DISTINCT Job) 'Работ в проекте' FROM Works_on GROUP BY ProjectNumber;
Здесь происходит подсчет количества разных должностей для каждого проекта. Результат выполнения этого запроса:

Как можно видеть в результате выполнения запроса, представленного в примере, значения NULL функцией COUNT не принимались во внимание. (Сумма всех значений столбца должностей получилась равной 7, а не 11, как должно быть.)
Вторая форма функции COUNT, т.е. функция COUNT(*) подсчитывает количество строк в таблице. А если инструкция SELECT запроса с функцией COUNT(*) содержит предложение WHERE с условием, функция возвращает количество строк, удовлетворяющих указанному условию. В отличие от первого варианта функции COUNT вторая форма не игнорирует значения NULL, поскольку эта функция оперирует строками, а не столбцами. В примере ниже демонстрируется использование функции COUNT(*):
USE SampleDb; SELECT Job AS 'Тип работ', COUNT(*) 'Нужно работников' FROM Works_on GROUP BY Job;
Здесь происходит подсчет количества должностей во всех проектах. Результат выполнения запроса:

Функция COUNT_BIG аналогична функции COUNT. Единственное различие между ними заключается в типе возвращаемого ими результата: функция COUNT_BIG всегда возвращает значения типа BIGINT, тогда как функция COUNT возвращает значения данных типа INTEGER.
Статистические агрегатные функции
Следующие функции составляют группу статистических агрегатных функций:
VAR
Вычисляет статистическую дисперсию всех значений, представленных в столбце или выражении.
VARP
Вычисляет статистическую дисперсию совокупности всех значений, представленных в столбце или выражении.
STDEV
Вычисляет среднеквадратическое отклонение (которое рассчитывается как квадратный корень из соответствующей дисперсии) всех значений столбца или выражения.
STDEVP
Вычисляет среднеквадратическое отклонение совокупности всех значений столбца или выражения.
Агрегатные функции, определяемые пользователем
Компонент Database Engine также поддерживает реализацию функций, определяемых пользователем. Эта возможность позволяет пользователям дополнить системные агрегатные функции функциями, которые они могут реализовывать и устанавливать самостоятельно. Эти функции представляют специальный класс определяемых пользователем функций и подробно рассматриваются позже.
Предложение HAVING
В предложении HAVING определяется условие, которое применяется к группе строк. Таким образом, это предложение имеет такой же смысл для групп строк, что и предложение WHERE для содержимого соответствующей таблицы. Синтаксис предложения HAVING следующий:
HAVING condition
Здесь параметр condition представляет условие и содержит агрегатные функции или константы.
Использование предложения HAVING совместно с агрегатной функцией COUNT(*) показано в примере ниже:
USE SampleDb; -- Вернет 'p3' SELECT ProjectNumber FROM Works_on GROUP BY ProjectNumber HAVING COUNT(*) < 4;
В этом примере посредством предложения GROUP BY система группирует все строки по значениям столбца ProjectNumber. После этого подсчитывается количество строк в каждой группе и выбираются группы, содержащие менее четырех строк (три или меньше).
Предложение HAVING можно также использовать без агрегатных функций, как это показано в примере ниже:
USE SampleDb; -- Вернет 'Консультант' SELECT Job FROM Works_on GROUP BY Job HAVING Job LIKE 'К%';
В этом примере происходит группирование строк таблицы Works_on по должности и устранение тех должностей, которые не начинаются с буквы "К".
Предложение HAVING можно также использовать без предложения GROUP BY, хотя это не является распространенной практикой. В таком случае все строки таблицы возвращаются в одной группе.
Предложение ORDER BY
Предложение ORDER BY определяет порядок сортировки строк результирующего набора, возвращаемого запросом. Это предложение имеет следующий синтаксис:
Порядок сортировки задается в параметре col_name. Параметр col_number является альтернативным указателем порядка сортировки, который определяет столбцы по порядку их вхождения в список выборки инструкции SELECT (1 - первый столбец, 2 - второй столбец и т.д.). Параметр ASC определяет сортировку в восходящем порядке, а параметр DESC - в нисходящем. По умолчанию применяется параметр ASC.
Имена столбцов в предложении ORDER BY не обязательно должны быть указаны в списке столбцов выборки. Но это не относится к запросам типа SELECT DISTINCT, т.к. в таких запросах имена столбцов, указанные в предложении ORDER BY, также должны быть указаны в списке столбцов выборки. Кроме этого, это предложение не может содержать имен столбцов из таблиц, не указанных в предложении FROM.
Как можно видеть по синтаксису предложения ORDER BY, сортировка результирующего набора может выполняться по нескольким столбцам. Такая сортировка показана в примере ниже:
USE SampleDb; SELECT * FROM Employee WHERE Id < 20000 ORDER BY LastName, FirstName;
В этом примере происходит выборка номеров отделов и фамилий и имен сотрудников для сотрудников, чей табельный номер меньше чем 20 000, а также с сортировкой по фамилии и имени. Результат выполнения этого запроса:

Столбцы в предложении ORDER BY можно указывать не по их именам, а по порядку в списке выборки. Соответственно, предложение в примере выше можно переписать таким образом:
USE SampleDb; SELECT * FROM Employee WHERE Id < 20000 ORDER BY 3, 2;
Такой альтернативный способ указания столбцов по их позиции вместо имен применяется, если критерий упорядочивания содержит агрегатную функцию. (Другим способом является использование наименований столбцов, которые тогда отображаются в предложении ORDER BY.) Однако в предложении ORDER BY рекомендуется указывать столбцы по их именам, а не по номерам, чтобы упростить обновление запроса, если в списке выборки придется добавить или удалить столбцы. Указание столбцов в предложении ORDER BY по их номерам показано в примере ниже:
USE SampleDb; SELECT ProjectNumber, COUNT(*) 'Количество сотрудников' FROM Works_on GROUP BY ProjectNumber ORDER BY 2 DESC;
Здесь для каждого проекта выбирается номер проекта и количество участвующих в нем сотрудников, упорядочив результат в убывающем порядке по числу сотрудников.
Язык Transact-SQL при сортировке в возрастающем порядке помещает значения NULL в начале списка, и в конце списка - при убывающем.
Использование предложения ORDER BY для разбиения результатов на страницы
Отображение результатов запроса на текущей странице можно или реализовать в пользовательском приложении, или же дать указание осуществить это серверу базы данных. В первом случае все строки базы данных отправляются приложению, чьей задачей является отобрать требуемые строки и отобразить их. Во втором случае, со стороны сервера выбираются и отображаются только строки, требуемые для текущей страницы. Как можно предположить, создание страниц на стороне сервера обычно обеспечивает лучшую производительность, т.к. клиенту отправляются только строки, необходимые для отображения.
Для поддержки создания страниц на стороне сервера в SQL Server 2012 вводится два новых предложения инструкции SELECT: OFFSET и FETCH. Применение этих двух предложений демонстрируется в примере ниже. Здесь из базы данных AdventureWorks2012 (которую вы можете найти в исходниках) извлекается идентификатор бизнеса, название должности и день рождения всех сотрудников женского пола с сортировкой результата по названию должности в возрастающем порядке. Результирующий набор строк разбивается на 10-строчные страницы и отображается третья страница:
USE AdventureWorks2012; SELECT BusinessEntityID, JobTitle, BirthDate FROM HumanResources.Employee WHERE Gender = 'F' ORDER BY JobTitle OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;
В предложении OFFSET указывается количество строк результата, которые нужно пропустить в отображаемом результате. Это количество вычисляется после сортировки строк предложением ORDER BY. В предложении FETCH NEXT указывается количество удовлетворяющих условию WHERE и отсортированных строк, которое нужно возвратить. Параметром этого предложения может быть константа, выражение или результат другого запроса. Предложение FETCH NEXT аналогично предложению FETCH FIRST.
Основной целью при создании страниц на стороне сервера является возможность реализация общих страничных форм, используя переменные. Эту задачу можно выполнить посредством пакета SQL Server.
Инструкция SELECT и свойство IDENTITY
Свойство IDENTITY позволяет определить значения для конкретного столбца таблицы в виде автоматически возрастающего счетчика. Это свойство могут иметь столбцы численного типа данных, такого как TINYINT, SMALLINT, INT и BIGINT. Для такого столбца таблицы компонент Database Engine автоматически создает последовательные значения, начиная с указанного стартового значения. Таким образом, свойство IDENTITY можно использовать для создания однозначных числовых значений для выбранного столбца.
Таблица может содержать только один столбец со свойством IDENTITY. Владелец таблицы имеет возможность указать начальное значение и шаг приращения, как это показано в примере ниже:
USE SampleDb; CREATE TABLE Product ( Id INT IDENTITY(10000, 1) NOT NULL, Name NVARCHAR(30) NOT NULL, Price MONEY ) INSERT INTO Product(Name, Price) VALUES ('Товар1', 10), ('Товар2', 15), ('Товар3', 8), ('Товар4', 15), ('Товар5', 40); -- Вернет 10004 SELECT IDENTITYCOL FROM Product WHERE Name = 'Товар5'; -- Аналог предыдущей инструкции SELECT $identity FROM Product WHERE Name = 'Товар5';
В этом примере сначала создается таблица Product, содержащая столбец Id со свойством IDENTITY. Значения в столбце Id создаются автоматически системой, начиная с 10 000 и увеличиваясь с единичным шагом для каждого последующего значения: 10 000, 10 001, 10 002 и т.д.
Со свойством IDENTITY связаны некоторые системные функции и переменные. Например, в коде примера используется системная переменная $identity. Как можно видеть по результатам выполнения этого кода, эта переменная автоматически ссылается на свойство IDENTITY. Вместо нее можно также использовать системную функцию IDENTITYCOL.
Начальное значение и шаг приращения столбца со свойством IDENTITY можно узнать с помощью функций IDENT_SEED и IDENT_INCR соответственно. Применяются эти функции следующим образом:
USE SampleDb; SELECT IDENT_SEED('Product'), IDENT_INCR('Product')
Как уже упоминалось, значения IDENTITY устанавливаются автоматически системой. Но пользователь может указать явно свои значения для определенных строк, присвоив параметру IDENTITY_INSERT значение ON перед вставкой явного значения:
SET IDENTITY INSERT table name ON
Поскольку с помощью параметра IDENTITY_INSERT для столбца со свойством IDENTITY можно установить любое значение, в том числе и повторяющееся, свойство IDENTITY обычно не обеспечивает принудительную уникальность значений столбца. Поэтому для принудительного обеспечения уникальности значений столбца следует применять ограничения UNIQUE или PRIMARY KEY.
При вставке значений в таблицу после присвоения параметру IDENTITY_INSERT значения on система создает следующее значение столбца IDENTITY, увеличивая наибольшее текущее значение этого столбца.
Оператор CREATE SEQUENCE
Применение свойства IDENTITY имеет несколько значительных недостатков, наиболее существенными из которых являются следующие:
- применение свойства ограничивается указанной таблицей;
- новое значение столбца нельзя получить иным способом, кроме как применив его;
- свойство IDENTITY можно указать только при создании столбца.
По этим причинам в SQL Server 2012 вводятся последовательности, которые обладают той же семантикой, что и свойство IDENTITY, но при этом не имеют ранее перечисленных недостатков. В данном контексте последовательностью называется функциональность базы данных, позволяющая указывать значения счетчика для разных объектов базы данных, таких как столбцы и переменные.
Последовательности создаются с помощью инструкции CREATE SEQUENCE. Инструкция CREATE SEQUENCE определена в стандарте SQL и поддерживается другими реляционными системами баз данных, такими как IBM DB2 и Oracle.
В пример ниже показано создание последовательности в SQL Server:
USE SampleDb; CREATE SEQUENCE dbo.Sequence1 AS INT START WITH 1 INCREMENT BY 5 MINVALUE 1 MAXVALUE 256 CYCLE;
В примере выше значения последовательности Sequence1 создаются автоматически системой, начиная со значения 1 с шагом 5 для каждого последующего значения. Таким образом, в предложении START указывается начальное значение, а в предложении INCREMENT - шаг. (Шаг может быть как положительным, так и отрицательным.)
В следующих двух, необязательных, предложениях MINVALUE и MAXVALUE указываются минимальное и максимальное значение объекта последовательности. (Обратите внимание, что значение MINVALUE должно быть меньшим или равным начальному значению, а значение MAXVALUE не может быть большим, чем верхний предел типа данных, указанных для последовательности.) В предложении CYCLE указывается, что последовательность повторяется с начала по превышению максимального (или минимального для последовательности с отрицательным шагом) значения. По умолчанию это предложение имеет значение NO CYCLE, что означает, что превышение максимального или минимального значения последовательности вызывает исключение.
Основной особенностью последовательностей является их независимость от таблиц, т.е. их можно использовать с любыми объектами базы данных, такими как столбцы таблицы или переменные. (Это свойство положительно влияет на хранение и, соответственно, на производительность. Определенную последовательность хранить не требуется; сохраняется только ее последнее значение.)
Новые значения последовательности создаются с помощью выражения NEXT VALUE FOR, применение которого показано в примере ниже:
USE SampleDb; -- Вернет 1 SELECT NEXT VALUE FOR dbo.sequence1; -- Вернет 6 (следующий шаг) SELECT NEXT VALUE FOR dbo.sequence1;
С помощью выражения NEXT VALUE FOR можно присвоить результат последовательности переменной или ячейке столбца. В примере ниже показано использование этого выражения для присвоения результатов столбцу:
USE SampleDb; CREATE TABLE Product ( Id INT NOT NULL, Name NVARCHAR(30) NOT NULL, Price MONEY ) INSERT INTO Product VALUES (NEXT VALUE FOR dbo.sequence1, 'Товар1', 10); INSERT INTO Product VALUES (NEXT VALUE FOR dbo.sequence1, 'Товар2', 15); -- .
В примере выше сначала создается таблица Product, состоящая из четырех столбцов. Далее, две инструкции INSERT вставляют в эту таблицу две строки. Первые две ячейки первого столбца будут иметь значения 11 и 16.
В примере ниже показано использование представления каталога sys.sequences для просмотра текущего значения последовательности, не используя его:
USE SampleDb; SELECT current_value FROM sys.sequences WHERE name = 'Sequence1';
Обычно выражение NEXT VALUE FOR применяется в инструкции INSERT, чтобы система вставляла созданные значения. Это выражение также можно использовать, как часть многострочного запроса с помощью предложения OVER.
Для изменения свойства существующей последовательности применяется инструкция ALTER SEQUENCE. Одно из наиболее важных применений этой инструкции связано с параметром RESTART WITH, который переустанавливает указанную последовательность. В примере ниже показано использование инструкции ALTER SEQUENCE для переустановки почти всех свойств последовательности Sequence1:
USE SampleDb; ALTER SEQUENCE dbo.sequence1 RESTART WITH 100 INCREMENT BY 50 MINVALUE 50 MAXVALUE 200 NO CYCLE;
Удаляется последовательность с помощью инструкции DROP SEQUENCE.
Операторы работы с наборами
Кроме операторов, рассмотренных ранее, язык Transact-SQL поддерживает еще три оператора работы с наборами: UNION, INTERSECT и EXCEPT.
Оператор UNION
Оператор UNION объединяет результаты двух или более запросов в один результирующий набор, в который входят все строки, принадлежащие всем запросам в объединении. Соответственно, результатом объединения двух таблиц является новая таблица, содержащая все строки, входящие в одну из исходных таблиц или в обе эти таблицы.
Общая форма оператора UNION выглядит таким образом:
select_1 UNION [ALL] select_2 <[UNION [ALL] select_3]>.
Параметры select_1, select_2, . представляют собой инструкции SELECT, которые создают объединение. Если используется параметр ALL, отображаются все строки, включая дубликаты. В операторе UNION параметр ALL имеет то же самое значение, что и в списке выбора SELECT, но с одним отличием: для списка выбора SELECT этот параметр применяется по умолчанию, а для оператора UNION его нужно указывать явно.
В своей исходной форме база данных SampleDb не подходит для демонстрации применения оператора UNION. Поэтому в этом разделе создается новая таблица EmployeeEnh, которая идентична существующей таблице Employee, но имеет дополнительный столбец City. В этом столбце указывается место жительства сотрудников.
Создание таблицы EmployeeEnh предоставляет нам удобный случай продемонстрировать использование предложения INTO в инструкции SELECT. Инструкция SELECT INTO выполняет две операции. Сначала создается новая таблица со столбцами, перечисленными в списке выбора SELECT. Потом строки исходной таблицы вставляются в новую таблицу. Имя новой таблицы указывается в предложении INTO, а имя таблицы-источника указывается в предложении FROM.
В примере ниже показано создание таблицы EmployeeEnh из таблицы Employee:
USE SampleDb; SELECT * INTO EmployeeEnh FROM Employee; ALTER TABLE EmployeeEnh ADD City NCHAR(40) NULL;
В этом примере инструкция SELECT INTO создает таблицу EmployeeEnh, вставляет в нее все строки из таблицы-источника Employee, после чего инструкция ALTER TABLE добавляет в новую таблицу столбец City. Но добавленный столбец City не содержит никаких значений. Значения в этот столбец можно вставить посредством среды Management Studio или же с помощью следующего кода:
USE SampleDb; UPDATE EmployeeEnh SET City = 'Казань' WHERE EmployeeEnh SET City = 'Москва' WHERE EmployeeEnh SET City = 'Екатеринбург' WHERE EmployeeEnh SET City = 'Санкт-Петербург' WHERE EmployeeEnh SET City = 'Краснодар' WHERE EmployeeEnh SET City = 'Казань' WHERE EmployeeEnh SET City = 'Пермь' WHERE >Теперь мы готовы продемонстрировать использование инструкции UNION. В примере ниже показан запрос для создания соединения таблиц EmployeeEnh и Department, используя эту инструкцию:
USE SampleDb; SELECT City AS 'Город' FROM EmployeeEnh UNION SELECT Location FROM Department;
Результат выполнения этого запроса:

Объединять с помощью инструкции UNION можно только совместимые таблицы. Под совместимыми таблицами имеется в виду, что оба списка столбцов выборки должны содержать одинаковое число столбцов, а соответствующие столбцы должны иметь совместимые типы данных. (В отношении совместимости типы данных INT и SMALLINT не являются совместимыми.)
Результат объединения можно упорядочить, только используя предложение ORDER BY в последней инструкции SELECT, как это показано в примере ниже. Предложения GROUP BY и HAVING можно применять с отдельными инструкциями SELECT, но не в самом объединении.
USE SampleDb; -- Вернет 18316, 28559 SELECT Id FROM Employee WHERE DepartamentNumber = 'd1' UNION SELECT EmpId FROM Works_on WHERE EnterDate > '01.01.2008' ORDER BY 1;
Запрос в этом примере осуществляет выборку сотрудников, которые или работают в отделе d1, или начали работать над проектом до 1 января 2008 г.
Оператор UNION поддерживает параметр ALL. При использовании этого параметра дубликаты не удаляются из результирующего набора. Вместо оператора UNION можно применить оператор OR, если все инструкции SELECT, соединенные одним или несколькими операторами UNION, ссылаются на одну и ту же таблицу. В таком случае набор инструкций SELECT заменяется одной инструкцией SELECT с набором операторов OR.
Операторы INTERSECT и EXCEPT
Два других оператора для работы с наборами, INTERSECT и EXCEPT, определяют пересечение и разность соответственно. Под пересечением в данном контексте имеется набор строк, которые принадлежат к обеим таблицам. А разность двух таблиц определяется как все значения, которые принадлежат к первой таблице и не присутствуют во второй. В примере ниже показано использование оператора INTERSECT:
USE SampleDb; -- Вернет только 28559 SELECT Id FROM Employee WHERE DepartamentNumber = 'd1' INTERSECT SELECT EmpId FROM Works_on WHERE EnterDate > '01.01.2008';
Язык Transact-SQL не поддерживает использование параметра ALL ни с оператором INTERSECT, ни с оператором EXCEPT. Использование оператора EXCEPT показано в примере ниже:
USE SampleDb; -- Вернет 10102, 25348 SELECT Id FROM Employee WHERE DepartamentNumber = 'd3' EXCEPT SELECT EmpId FROM Works_on WHERE EnterDate > '01.01.2008';
Следует помнить, что эти три оператора над множествами имеют разный приоритет выполнения: оператор INTERSECT имеет наивысший приоритет, за ним следует оператор EXCEPT, а оператор UNION имеет самый низкий приоритет. Невнимательность к приоритету выполнения при использовании нескольких разных операторов для работы с наборами может повлечь неожиданные результаты.
Выражения CASE
В области прикладного программирования баз данных иногда требуется модифицировать представление данных. Например, людей можно подразделить, закодировав их по их социальной принадлежности, используя значения 1, 2 и 3, обозначив так мужчин, женщин и детей соответственно. Такой прием программирования может уменьшить время, необходимое для реализации программы. Выражение CASE языка Transact-SQL позволяет с легкостью реализовать такой тип кодировки.
В отличие от большинства языков программирования, CASE не является инструкцией, а выражением. Поэтому выражение CASE можно использовать почти везде, где язык Transact-SQL позволяет применять выражения. Выражение CASE имеет две формы:
- простое выражение CASE;
- поисковое выражение CASE.
Синтаксис простого выражения CASE следующий:
Инструкция с простым выражением CASE сначала ищет в списке всех выражений в предложении WHEN первое выражение, совпадающее с выражением expression_1, после чего выполняет соответствующее предложение THEN. В случае отсутствия в списке WHEN совпадающего выражения, выполняется предложение ELSE.
Синтаксис поискового выражения CASE следующий:
В данном случае выполняется поиск первого отвечающего требованиям условия, после чего выполняется соответствующее предложение THEN. Если ни одно из условий не отвечает требованиям, выполняется предложение ELSE. Применение поискового выражения CASE показано в примере ниже:
USE SampleDb; SELECT ProjectName, CASE WHEN Budget > 0 AND Budget 100000 AND Budget 150000 AND Budget
Результат выполнения этого запроса:

В этом примере взвешиваются бюджеты всех проектов, после чего отображаются вычисленные их весовые коэффициенты вместе с соответствующими наименованиями проектов.
В примере ниже показан другой способ применения выражения CASE, где предложение WHEN содержит вложенные запросы, составляющие часть выражения:
USE SampleDb; SELECT ProjectName, CASE WHEN p1.Budget < (SELECT AVG(p2.Budget) FROM Project p2) THEN 'ниже среднего' WHEN p1.Budget <=(SELECT AVG(p2.Budget) FROM Project p2) THEN 'равен среднему' WHEN p1.Budget >(SELECT AVG(p2.Budget) FROM Project p2) THEN 'выше среднего' END 'Категория бюджета' FROM Project p1;
Результат выполнения этого запроса следующий:
Какие агрегатные функции можно использовать в операторе select
В статье об группировках мы обсудили, что при использовании оператора GROUP BY мы можем использовать агрегатные функции. Давайте поговорим о них поглубже .
Агрегатная функция – это функция, которая выполняет вычисление на наборе значений и возвращает одиночное значение.
MySQLSELECT [литералы, агрегатные_функции, поля_группировки] FROM имя_таблицы GROUP BY поля_группировки;Например, запрос с использованием агрегатной функции AVG может выглядеть так:
MySQLSELECT home_type, AVG(price) as avg_price FROM Rooms GROUP BY home_type
home_type avg_price Private room 89.4286 Entire home/apt 148.6667 Shared room 40