Как посчитать уникальные значения в sql
Перейти к содержимому

Как посчитать уникальные значения в sql

  • автор:

TablePlus

How to count unique rows in MySQL with COUNT DISTINCT?

Overview

In MySQL, COUNT() function returns the number of rows in a table.

COUNT(DISTINCT expression) returns the number of rows that contain non NULL values as the result of the specified expression.

Syntax

To to use COUNT(DISTINCT) in a SELECT statement:

Example

We have table sales :

item customer_name
book Alex
pen Bob
book Alex
book Jim
shoes Jim
pen Bob
backpack Bob
book Bob
wallet Alex
wallet Alex
book Bob
backpack Jim

Since a customer might have multiple purchases, the customer_name field has duplicate values. So to count the number of unique customers:

The result is 3.

A customer might purchased one item multiple times, to see how many unique items each customer already purchased, we can use GROUP BY :

customer_name unique_item
Alex 2
Bob 3
Jim 3

We can also include WHERE to count the number of unique customers that bought the item book :

The result is 3.

Need a good GUI tool for databases? TablePlus provides a native client that allows you to access and manage Oracle, MySQL, SQL Server, PostgreSQL, and many other databases simultaneously using an intuitive and powerful graphical interface.

Как посчитать уникальные значения в sql

Аватар пользователя Roman Ashikov

Используем DISTINCT и COUNT() . Первый удалит дубликаты, а вторая посчитает количество строк.

Рекомендуемые курсы

Похожие вопросы

  • О нас
  • Карьера в Хекслете
  • Хекслет Колледж
  • Условия использования
  • Соглашение об обработке ПД
  • Оферта
  • Акции
  • 8 800 100 22 47 бесплатно по РФ
  • +7 495 085 28 38 бесплатно по Москве
  • support@hexlet.io

ООО «Хекслет Рус» 432071, г. Ульяновск, пр-т Нариманова, дом 1Г, оф. 23 ОГРН 1217300010476

Подсчет уникальных значений

Есть таблица org (name, emp_id). Для каждого возможного значения name , необходимо вывести количество уникальных emp_id . Можно ли это сделать с помощью count() или как ещё?

UPD: Немного расшифрую. Допустим

В результате необходимо получить что-то вроде

т.е. если emp_id повторяется в разных name , то их необходимо подсчитывать

Первая интерпретация вашего вопроса Для каждого name подсчитать уникальное количество emp_id

Вторая интерпретация, подсчитать так, что бы исключить emp_id встречающиеся у разных name Для этого пойдем от обратного подсчитаем количество name у emp_id , то есть нам необходимо исключить из выборки emp_id встречающиеся у нескольких name

SQL HowTo: считаем «уников» на интервале

Для систем управления бизнесом часто приходится решать очень похожий класс задач по вычислению количества уникальных объектов на произвольном временном интервале. В контексте CRM это могут быть «пользователи, обращавшиеся на горячую линию на прошлой неделе«, «контрагенты, оплатившие за последние 30 дней» или «потенциальные клиенты, с кем был контакт в этом квартале«.

Искать в большом количестве фактов «уники» — всегда сложно и долго, если их достаточно много. Если интервалы фиксированы (календарные месяц/квартал/год), можно материализовывать такие агрегаты заранее. А если интервал — произвольный, как тогда эффективно найти ответ?

Сначала смоделируем ситуацию, как она выглядит для нас, в масштабах «Тензора» — 5000 наших сотрудников до 100 000 раз за сутки общаются с потенциальными клиентами или с кем-то из уже работающих с нами 5 миллионов пользователей:

А теперь попробуем ответить на простой вопрос — сколько было уникальных клиентов в декабре?

Понятно, что если вся «первичка» уже лежит в PostgreSQL, то для реализации одной лишь этой функции выгружать данные во внешнюю СУБД с колоночным хранением данных вроде ClickHouse, которая подошла бы именно для этой задачи лучше, не особо оправданно с точки зрения эксплуатации разнородной архитектуры.

Получилось чуть больше 1364K «уников» из 1594K «фактов», но считалось это как-то слишком долго — 717мс [explain.tensor.ru]:

"Наивный" подсчет по первичным данным

«Наивный» подсчет по первичным данным

Но самое печальное — это объем данных, который нам приходится прочитать — почти 23K страниц, то есть примерно 180MB. И если любая из них окажется не в кэше, и ее придется вычитывать с диска — тормоза нам обеспечены.

ARRAY

Но мы можем попробовать заранее «упаковать» имеющиеся у нас данные, чтобы в момент запроса читать самый минимум — использовать массив уникальных идентификаторов за каждый день (ведь нам заранее неизвестен интервал, который захочет запросить пользователь):

Тогда нам останется лишь прочитать записи за нужный интервал дней, «развернуть» все идентификаторы и посчитать количество уникальных:

Читать приходится в 26 раз меньше — всего 888 страниц, да и по времени отыграли 10% — до 642мс [explain.tensor.ru]:

Группировка массивов

Группировка массивов

hstore

Но в варианте с массивом нам пришлось каждый из них unnest ‘ить и уникализировать самостоятельно? Но ведь есть способ отдать уникализацию самому серверу — использовать сложение hstore с одноименными ключами:

Не забываем, что сначала необходимо установить это расширение в свою базу:

И только после этого пакуем, не забывая, что все ключи должны быть текстовыми:

Теперь нам необходимо «сложить» hstore -объекты в нужных строках, но соответствующей агрегатной функции hstore_agg не существует. Не беда — создадим ее сами:

Воспользуемся функцией akeys , которая вернет нам массив ключей собранного объекта:

Читать теперь приходится в 2.5 раза больше — 3049 страниц, зато по времени — еще минус 5% — 612мс [explain.tensor.ru]:

Группировка hstore

Группировка hstore

jsonb

Но ведь есть «нативный» тип, который самостоятельно делает уникализацию ключей — json[b]:

Давайте обойдемся без всех этих дополнительно устанавливаемых расширений:

Правда, агрегатную функцию снова придется делать самим:

И даже не будем пробовать извлекать ключи — достаточно уже агрегации.

. чтобы понять, что хоть jsonb и гораздо компактнее hstore (читать приходится лишь чуть больше, чем для массивов — 963 страницы), но это не окупает адских тормозов при агрегации почти на 13 секунд! [explain.tensor.ru]:

Группировка jsonb

Группировка jsonb

Итак, в сегодняшнем забеге упаковка в массив позволяет получить наиболее сбалансированный по объему и скорости результат.

А еще быстрее — можно? Да, но это уже материал для хаба «ненормальное программирование».

Основные затраты времени у нас шли на объединение массивов/объектов. То есть чем меньше записей с «пакетами» нам необходимо обработать — тем лучше.

Для этого все даты можно покрыть отрезками длиной 2^N :

Сегментация дат

Сегментация дат

Тогда любой из заданных пользователем интервалов можно разбить на такие непересекающиеся отрезки:

Разбиение интервала на сегменты

Разбиение интервала на сегменты

Ну, а каскадное добавление ID клиента во все такие отрезки можно с помощью несложной функции:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *