Как и большинство других реляционных систем баз данных,
Digital Q.DataBase поддерживает
агрегатные функции.
Агрегатная функция вычисляет один результат из нескольких входных строк.
Например, существуют агрегаты для вычисления
count (количество), sum (сумма),
avg (среднее), max (максимум) и
min (минимум) над набором строк.
В качестве примера мы можем найти самую высокую минимальную температуру где-либо с помощью:
SELECT max(temp_lo) FROM weather;
max ----- 46 (1 row)
Если бы мы хотели знать, в каком городе (или городах) это показание было зафиксировано, мы могли бы попробовать:
SELECT city FROM weather WHERE temp_lo = max(temp_lo); WRONG
но это не сработает, поскольку агрегатная функция
max не может использоваться в предложении
WHERE. (Это ограничение существует, потому что
предложение WHERE определяет, какие строки будут
включены в агрегатное вычисление; очевидно, оно должно быть вычислено
до вычисления агрегатных функций.)
Однако, как это часто бывает,
запрос можно переформулировать для достижения желаемого результата, здесь
с помощью подзапроса (subquery):
SELECT city FROM weather
WHERE temp_lo = (SELECT max(temp_lo) FROM weather);
city
---------------
San Francisco
(1 row)
Это нормально, потому что подзапрос является независимым вычислением, которое вычисляет свой собственный агрегат отдельно от того, что происходит во внешнем запросе.
Агрегаты также очень полезны в сочетании с предложением GROUP
BY. Например, мы можем получить количество показаний
и максимальную минимальную температуру, наблюдаемую в каждом городе, с помощью:
SELECT city, count(*), max(temp_lo)
FROM weather
GROUP BY city;
city | count | max
---------------+-------+-----
Hayward | 1 | 37
San Francisco | 2 | 46
(2 rows)
что даёт нам одну выходную строку для каждого города. Каждый агрегатный результат
вычисляется по строкам таблицы, соответствующим этому городу.
Мы можем фильтровать эти сгруппированные
строки, используя HAVING:
SELECT city, count(*), max(temp_lo)
FROM weather
GROUP BY city
HAVING max(temp_lo) < 40;
city | count | max ---------+-------+----- Hayward | 1 | 37 (1 row)
что даёт нам те же результаты только для городов, у которых все
значения temp_lo ниже 40. Наконец, если нас интересуют только
города, имена которых
начинаются с «S», мы можем сделать:
SELECT city, count(*), max(temp_lo)
FROM weather
WHERE city LIKE 'S%' -- (1)
GROUP BY city;
city | count | max
---------------+-------+-----
San Francisco | 2 | 46
(1 row)
Оператор |
Важно понимать взаимодействие между агрегатами и
предложениями SQL WHERE и HAVING.
Фундаментальное различие между WHERE и
HAVING заключается в следующем: WHERE выбирает
входные строки до вычисления групп и агрегатов (таким образом, он контролирует,
какие строки попадают в агрегатное вычисление), тогда как
HAVING выбирает групповые строки после вычисления групп и
агрегатов. Таким образом,
предложение WHERE не должно содержать агрегатных функций;
бессмысленно пытаться использовать агрегат для определения того, какие строки
будут входами для агрегатов. С другой стороны, предложение
HAVING всегда содержит агрегатные функции.
(Строго говоря, вам разрешено писать предложение HAVING,
которое не использует агрегаты, но это редко полезно. То же
условие может быть использовано более эффективно на этапе WHERE.)
В предыдущем примере мы можем применить ограничение по имени города в
WHERE, поскольку оно не требует агрегата. Это
более эффективно, чем добавлять ограничение в HAVING,
потому что мы избегаем выполнения группировки и агрегатных вычислений
для всех строк, которые не проходят проверку WHERE.
Другой способ выбрать строки, которые попадают в агрегатное
вычисление, — использовать FILTER, который является
опцией для каждого агрегата:
SELECT city, count(*) FILTER (WHERE temp_lo < 45), max(temp_lo)
FROM weather
GROUP BY city;
city | count | max
---------------+-------+-----
Hayward | 1 | 37
San Francisco | 1 | 46
(2 rows)
FILTER очень похож на WHERE,
за исключением того, что он удаляет строки только из ввода конкретной
агрегатной функции, к которой он прикреплён.
Здесь агрегат count подсчитывает только
строки с temp_lo ниже 45; но агрегат
max по-прежнему применяется ко всем строкам,
поэтому он всё ещё находит показание 46.