В чем отличие от точки зрения простого удаления дубликатов
Помимо того, что в отличие от DISTINCT
, GROUP BY
допускает агрегирование данных по группе (о чем говорилось во многих других ответах), самым важным отличием, на мой взгляд, является тот факт, что две операции «происходят» на двух очень разных шагах в логическом порядке операций, которые выполняются в операторе SELECT
.
Вот наиболее важные операции:
FROM
(включая JOIN
, APPLY
и т. Д.)
WHERE
GROUP BY
(можно удалить дубликаты)
- Скопления
HAVING
- Оконные функции
SELECT
DISTINCT
(можно удалить дубликаты)
UNION
, INTERSECT
, EXCEPT
(можно удалить дубликаты)
ORDER BY
OFFSET
LIMIT
Как видите, логический порядок каждой операции влияет на то, что можно сделать с ней, и как она влияет на последующие операции. В частности, тот факт, что операция GROUP BY
«происходит раньше» операция SELECT
(проекция) означает, что:
- Это не зависит от проекции (что может быть преимуществом)
- Он не может использовать какие-либо значения из проекции (что может быть недостатком)
1. Это не зависит от проекции
Пример, в котором полезно не зависеть от проекции, - это если вы хотите рассчитать оконные функции по различным значениям:
SELECT rating, row_number() OVER (ORDER BY rating) AS rn
FROM film
GROUP BY rating
При запуске с базой данных Sakila это дает:
rating rn
-----------
G 1
NC-17 2
PG 3
PG-13 4
R 5
То же самое не может быть достигнуто с помощью DISTINCT
легко:
SELECT DISTINCT rating, row_number() OVER (ORDER BY rating) AS rn
FROM film
Этот запрос "неправильный" и выдает что-то вроде:
rating rn
------------
G 1
G 2
G 3
...
G 178
NC-17 179
NC-17 180
...
Это не то, что мы хотели. Операция DISTINCT
«происходит после» проекции, поэтому мы больше не можем удалять оценки DISTINCT
, поскольку оконная функция уже рассчитана и спроецирована. Чтобы использовать DISTINCT
, нам нужно вложить эту часть запроса:
SELECT rating, row_number() OVER (ORDER BY rating) AS rn
FROM (
SELECT DISTINCT rating FROM film
) f
Примечание: В данном конкретном случае мы также можем использовать DENSE_RANK()
SELECT DISTINCT rating, dense_rank() OVER (ORDER BY rating) AS rn
FROM film
2. Он не может использовать какие-либо значения из проекции
Одним из недостатков SQL является его многословность. По той же причине, что мы видели раньше (а именно, логический порядок операций), мы не можем «легко» группировать то, что проецируем.
Это недопустимый SQL:
SELECT first_name || ' ' || last_name AS name
FROM customer
GROUP BY name
Это верно (повторяет выражение)
SELECT first_name || ' ' || last_name AS name
FROM customer
GROUP BY first_name || ' ' || last_name
Это также верно (вложенное выражение)
SELECT name
FROM (
SELECT first_name || ' ' || last_name AS name
FROM customer
) c
GROUP BY name
Я написал об этой теме более подробно в блоге