SQL distinct: описание, примеры, свойства
Зачастую при использовании SQL для выборки информации из таблиц, пользователь получает избыточные данные, заключающиеся в наличии абсолютно идентичных повторяющихся строк. Для исключения этой ситуации используется аргумент SQL distinct в предложении Select. В данной статье будут рассмотрены примеры использования данного аргумента, а также ситуации, в которых от применения аргумента лучше отказаться.
Прежде чем мы приступим к рассмотрению конкретных примеров, создадим в базе данных пару необходимых таблиц.
Подготовка таблиц
Представим, что у нас в базе данных хранится информация об обоях, представленная в двух таблицах. Это таблица Oboi (обои) с полями id (уникальный идентификатор), type (тип обоев – бумажные, виниловые и др.), color (цвет), struct (структура) и price (цена). И таблица Ostatki (остатки) с полями id_oboi (ссылка на уникальный идентификатор в таблице Oboi) и count (количество рулонов на складе).
Заполним таблицы данными. В таблицу с обоями добавим 9 записей:
Oboi | ||||
id | type | color | struct | price |
1 | Бумажные | Мультиколор | Тисненые | 56,9 |
2 | Бумажные двухслойные | Беж | Гладкая | 114,8 |
3 | Виниловые | Оранж | Тисненые | 504 |
4 | Флизелиновые | Беж | Тисненые | 1020,9 |
5 | Бумажные двухслойные | Беж | Гладкая | 150,6 |
6 | Бумажные | Мультиколор | Гладкая | 95,4 |
7 | Виниловые | Коричневые | Гладкая | 372 |
8 | Флизелиновые | Белые | Тисненые | 980,1 |
9 | Тканевые | Розовые | Гладкая | 1166,5 |
В таблицу с остатками – также девять записей:
Ostatki | |
id_oboi | count |
1 | 8 |
2 | 12 |
3 | 24 |
4 | 9 |
5 | 16 |
6 | 7 |
7 | 24 |
8 | 32 |
9 | 11 |
Приступим к описанию порядка использования distinct в SQL.
Место distinct в предложении Select
Аргумент distinct следует помещать сразу после ключевого слова Select в запросах. Он применяется сразу ко всем столбцам, указанным в предложении Select, потому что будет исключать из итогового результата запроса абсолютно идентичные строки. Таким образом, достаточно один раз указать при написании запроса SQL «select distinct». Исключение составляет использование distinct внутри агрегатных функций, что рассмотрим чуть позднее.
Следует помнить, что большинство СУБД и не распознает ваш запрос вида:
SELECT distinct Ostatki.Count, distinct Oboi.* FROM Oboi INNER JOIN Ostatki ON Oboi.id = Ostatki.id_oboi |
Здесь несколько раз указан рассматриваемый аргумент либо указан один раз, но перед вторым, третьим или иным выбираемым столбцом. Вы получите ошибку со ссылкой на неточности в синтаксисе.
Применение distinct в стандартных запросах
Очевидно, что при грамотном построении структуры таблиц и их заполнении, внутри одной таблицы исключены ситуации, когда встречаются абсолютно идентичные строки. Поэтому выполнение запроса «Select distinct *» с выборкой из одной таблицы практически нецелесообразно.
Представим ситуацию, когда нам необходимо узнать, какого типа есть у нас обои, сразу для удобства выполним сортировку по типу:
SELECT Oboi.type FROM Oboi order by type |
И получим результат:
type |
Бумажные |
Бумажные |
Бумажные двухслойные |
Бумажные двухслойные |
Виниловые |
Виниловые |
Тканевые |
Флизелиновые |
Флизелиновые |
Как видим, в таблице присутствуют дублирующиеся строки. Если же мы добавим в предложение Select distinct:
SELECT distinct Oboi.type FROM Oboi order by type |
то получим результат без повторов:
type |
Бумажные |
Бумажные двухслойные |
Виниловые |
Тканевые |
Флизелиновые |
Таким образом, если грамотно вносились данные в таблицы, то сразу по звонку или запросу покупателей мы сможем ответить, что жидких обоев, стеклообоев и акриловых обоев в наличии в магазине нет. Учитывая, что ассортимент в магазинах обычно не ограничивается одной сотней обоев, просмотреть перечень из неуникальных типов было бы довольно трудозатратно.
Применение distinct внутри агрегатных функций
Аргумент SQL distinct можно использовать с любой агрегатной функцией. Но для Min и Max его применение не даст никакого эффекта, а при вычислении суммы или среднего значения редко можно представить ситуацию, когда не нужно было бы учитывать повторы.
Допустим, мы хотим узнать, насколько заполнен наш склад, и для этого отправляем запрос, вычисляющий общее количество рулонов на складе:
SELECT sum(Ostatki.count) FROM Ostatki |
Запрос выдаст ответ 143. Если же мы изменим на:
SELECT sum(distinct Ostatki.count) FROM Ostatki |
то получим всего 119, ведь обои под артикулами 3 и 7 находятся на складе в одинаковом количестве. Однако очевидно, что этот ответ неверен.
Чаще всего в SQL distinct применяется с функцией Count. Так, без труда мы можем узнать, сколько уникальных видов обоев у нас вообще есть:
SELECT count(distinct Oboi.type) FROM Oboi |
И получить результат 5 – бумажные обычные и двухслойные, виниловые, тканевые и флизелиновые. Наверняка все видели рекламу типа: «Только у нас более 20 видов различных обоев!», под которой подразумевается, что в данном магазине не пара десятков рулонов всего, а обои самых разнообразных современных типов.
Интересно, что в одном запросе можно указывать несколько функций Count как с атрибутом distinct, так и без него. То есть это единственная ситуация, когда distinct в Select'е может присутствовать несколько раз.
Когда следует отказаться от применения аргумента
От применения аргумента SQL distinct следует отказаться в одном из двух случаев:
- Вы выполняете выборку из таблиц и уверены в уникальности значений в каждой. В таком случае применение аргумента нецелесообразно, ведь это дополнительная нагрузка на сервер или клиента (в зависимости от вида СУБД).
- Вы боитесь потерять нужные данные. Поясним.
Допустим, начальник просит вас вывести список обоев, которые у вас есть, с указанием всего двух столбцов – тип и цвет. По привычке вы указываете аргумент distinct:
SELECT distinct Oboi.type, Oboi.color FROM Oboi ORDER BY Oboi.type |
И – теряете часть данных:
type | color |
Бумажные | Мультиколор |
Бумажные двухслойные | Беж |
Виниловые | Коричневые |
Виниловые | Оранж |
Тканевые | Розовые |
Флизелиновые | Беж |
Флизелиновые | Белые |
Может создаться впечатление, что бумажных обоев (обычных и двухслойных) у нас всего по одному виду, хотя на самом деле даже в нашей маленькой таблице их по два артикула (результат без distinct):
type | color |
Бумажные | Мультиколор |
Бумажные | Мультиколор |
Бумажные двухслойные | Беж |
Бумажные двухслойные | Беж |
Виниловые | Коричневые |
Виниловые | Оранж |
Тканевые | Розовые |
Флизелиновые | Белые |
Флизелиновые | Беж |
Поэтому, как и при написании любого запроса, с аргументом distinct надо быть аккуратным и грамотно решать вопрос с ее применением в зависимости от поставленной задачи.
Альтернатива distinct
Противоположность аргументу distinct – аргумент All. При его применении повторяющиеся строки сохраняются. Но поскольку по умолчанию СУБД так и считает, что нужно выводить все значения, то аргумент All – это скорее уточнитель, чем реальный функциональный аргумент.