Когда пишешь запросы к реальным таблицам, дубликаты появляются быстрее, чем ожидаешь: один и тот же город у десятка клиентов, повторяющиеся категории, множество NULL в одном столбце. Предложение DISTINCT решает эту задачу в одну строку — и в этой статье я разберу, как именно оно работает и где его применение оправдано

Синтаксис SELECT DISTINCT в SQLite
Базовый синтаксис выглядит так:
SELECT DISTINCT select_list
FROM table;
Разберём ключевые правила:
Во-первых, предложение DISTINCT должно стоять сразу после ключевого слова SELECT
После DISTINCT указывается один столбец или несколько столбцов. Если выбран только один столбец, SQLite оценивает дублирование по его значениям. При указании нескольких столбцов SQLite анализирует комбинации всех этих значений
SQLite считает значения NULL дубликатами. Если применить DISTINCT к столбцу, содержащему несколько NULL, в результате останется только одна строка с NULL
В теории баз данных значение NULL в столбце означает, что информация об этом поле для конкретной записи отсутствует или неприменима. Например, если у клиента номер телефона равен NULL, это может означать, что номер не был известен на момент записи, либо у клиента вообще нет телефона
По этой теме полезно отдельно посмотреть EXPLAIN QUERY PLAN: план выполнения SQL-запроса в SQLite, чтобы расширить контекст и сравнить подходы
По этой теме полезно отдельно посмотреть Создание Flutter-приложения с SQLite, BLoC и Streams, чтобы расширить контекст и сравнить подходы
Пример: уникальные города клиентов
Для демонстрации я буду использовать таблицу customers из примера базы данных SQLite
Предположим, нужно узнать, в каких городах находятся клиенты. Сначала выполним запрос без DISTINCT:
SELECT city
FROM customers
ORDER BY city;
Запрос возвращает 59 строк с повторяющимися значениями, такими как Berlin, London и Mountain View. Для устранения дублирования добавляем DISTINCT
SELECT DISTINCT city
FROM customers
ORDER BY city;
Теперь результат содержит 53 строки: предложение DISTINCT удалило 6 дублирующихся строк
Пример: DISTINCT по нескольким столбцам
Следующий запрос выбирает города и страны всех клиентов:
SELECT city, country
FROM customers
ORDER BY country;
Результирующий набор включает дублирующиеся комбинации городов и стран, например, Sao Paulo в Бразилии встречается несколько раз. Для получения уникальных пар используем DISTINCT для обоих столбцов
SELECT DISTINCT city, country
FROM customers
ORDER BY country;
Как я уже отмечал выше, SQLite оценивает дублирование по комбинации значений city и country. Две строки считаются дубликатами только тогда, когда совпадают оба поля одновременно
Как DISTINCT обрабатывает NULL в SQLite
Рассмотрим столбец company в таблице customers:
SELECT company
FROM customers;
Запрос возвращает 59 строк, среди которых много значений NULL. Применим DISTINCT:
SELECT DISTINCT company
FROM customers;
В результате остаётся 11 строк, и NULL встречается только один раз. SQLite группирует все повторяющиеся NULL в одну строку, что соответствует ожидаемому поведению
DISTINCT или GROUP BY: когда что выбрать
Для получения уникальных комбинаций нескольких столбцов с использованием агрегатных функций лучше предпочесть GROUP BY, поскольку DISTINCT удаляет дубликаты на уровне строк, тогда как GROUP BY позволяет группировать строки
Пример с GROUP BY для сравнения:
SELECT city, COUNT(*) AS customer_count
FROM customers
GROUP BY city
ORDER BY city;
Такой запрос не только покажет уникальные города, но и сообщит, сколько клиентов в каждом из них
Типичные ошибки при работе с DISTINCT
На практике я встречал несколько распространённых заблуждений, которые стоит разобрать отдельно
DISTINCT применяется ко всему списку столбцов как единому целому, гарантируя уникальные пары, например, (город, страна), а не отдельные уникальные значения для каждого столбца
DISTINCT не заменяет индексы. На больших таблицах запрос с DISTINCT может работать медленно при отсутствии подходящего индекса по столбцам выборки. Оптимизацию следует проводить с использованием плана выполнения запроса, проверив его с помощью EXPLAIN QUERY PLAN
Важно понимать, что NULL и пустая строка не являются одним и тем же значением; DISTINCT оставит одну строку с NULL, отдельной от строк с пустой строкой ''
Предложение
SELECT DISTINCTв SQLite — простой и надёжный способ получить уникальные значения из одного или нескольких столбцов. Главное помнить:DISTINCTработает со всем набором перечисленных столбцов, аNULLтрактуется как дубликат и схлопывается в одну строку
Ответы на эти вопросы могут быть для вас полезными
Можно ли использовать DISTINCT с агрегатными функциями? Да. Например, SELECT COUNT(DISTINCT city) FROM customers вернёт количество уникальных городов, а не общее число строк
Влияет ли порядок столбцов после DISTINCT на результат? Нет, порядок столбцов в списке SELECT DISTINCT не влияет на то, какие строки считаются дубликатами — важна только комбинация значений
Как DISTINCT обрабатывает несколько значений NULL в одном столбце? SQLite считает все NULL одинаковыми, поэтому в результате останется только одна строка с NULL, остальные будут удалены
Чем DISTINCT отличается от GROUP BY без агрегации? На практике SELECT DISTINCT col FROM t и SELECT col FROM t GROUP BY col дают одинаковый результат. Разница проявляется при добавлении агрегатных функций: GROUP BY их поддерживает, DISTINCT — нет
Работает ли DISTINCT в подзапросах? Да. DISTINCT можно использовать внутри подзапроса точно так же, как и в основном запросе, — синтаксис и поведение не меняются



