Как посчитать COUNT DISTINCT в SQL

Карьерник — квиз-тренажёр в Telegram с 1500+ вопросами для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.

Базовый синтаксис

SELECT COUNT(DISTINCT column) FROM TABLE;

1. Уникальные пользователи

SELECT COUNT(DISTINCT user_id) AS unique_users
FROM events;

2. По комбинации колонок

-- уникальные пары (user_id, product_id)
SELECT COUNT(DISTINCT user_id, product_id) FROM orders;  -- MySQL

-- Postgres (другой синтаксис)
SELECT COUNT(DISTINCT (user_id, product_id)) FROM orders;

-- универсально — concat или CTE
SELECT COUNT(DISTINCT user_id || '|' || product_id) FROM orders;

3. Уникальные по условию

SELECT
    COUNT(DISTINCT CASE WHEN status = 'paid' THEN user_id END) AS paying_users,
    COUNT(DISTINCT CASE WHEN status = 'free'  THEN user_id END) AS free_users
FROM users;

4. Уникальные по группам

SELECT
    category,
    COUNT(DISTINCT user_id) AS unique_users
FROM orders
GROUP BY category;

5. DAU / MAU через COUNT DISTINCT

-- DAU
SELECT DATE(event_at) AS day, COUNT(DISTINCT user_id) AS dau
FROM events
GROUP BY 1;

-- MAU (скользящие 30 дней)
SELECT COUNT(DISTINCT user_id) AS mau
FROM events
WHERE event_at >= NOW() - INTERVAL '30 days';

6. Approximate count distinct (для больших данных)

Точный COUNT DISTINCT требует просмотра всех значений → медленно.

Алгоритм HyperLogLog (HLL) даёт приблизительный ответ с погрешностью ~1% за O(1) памяти.

ClickHouse

-- точно
SELECT COUNT(DISTINCT user_id) FROM events;

-- быстро (примерно)
SELECT uniq(user_id) FROM events;

-- ещё быстрее, ещё менее точно
SELECT uniqHLL12(user_id) FROM events;

BigQuery

SELECT APPROX_COUNT_DISTINCT(user_id) FROM events;

Snowflake

SELECT APPROX_COUNT_DISTINCT(user_id) FROM events;

Postgres

Нет встроенного. Через extension postgresql-hll.

7. COUNT(DISTINCT) vs COUNT(*)

SELECT
    COUNT(*) AS total_events,                 -- все события
    COUNT(DISTINCT user_id) AS unique_users   -- уникальные пользователи
FROM events;

Если у одного user 10 events — total = 10, unique = 1.

8. COUNT(*) vs COUNT(column)

COUNT(*)            -- все строки, включая NULL
COUNT(column)       -- только строки, где column IS NOT NULL
COUNT(DISTINCT col) -- уникальные не-NULL значения

9. Уникальные с фильтром даты

SELECT COUNT(DISTINCT user_id)
FROM events
WHERE event_at >= '2026-01-01'
  AND event_at <  '2026-02-01';

10. Динамика уникальных по дням

SELECT
    DATE(event_at) AS day,
    COUNT(DISTINCT user_id) AS daily_unique_users,
    SUM(COUNT(DISTINCT user_id)) OVER (ORDER BY DATE(event_at)) AS cumulative
FROM events
GROUP BY 1;

Внимание: cumulative через SUM не совпадёт с total unique (разные метрики).

11. Уникальные X за каждые Y

-- дневная DAU в каждом месяце
SELECT
    DATE_TRUNC('month', event_at) AS month,
    DATE(event_at) AS day,
    COUNT(DISTINCT user_id) AS dau
FROM events
GROUP BY 1, 2;

12. Проблема COUNT DISTINCT на миллиардах

-- медленно на больших данных
SELECT COUNT(DISTINCT user_id) FROM events;  -- может занять часы

-- быстрее через approximate
SELECT uniq(user_id) FROM events;             -- секунды

Для аналитики ±1% обычно ок.

13. В оконных функциях

-- нестандарт, не везде работает
COUNT(DISTINCT user_id) OVER (PARTITION BY category)

-- обычно обходят через CTE
WITH per_category AS (
    SELECT category, COUNT(DISTINCT user_id) AS uniq
    FROM orders
    GROUP BY category
)
SELECT o.*, pc.uniq
FROM orders o
JOIN per_category pc ON pc.category = o.category;

Частые ошибки

1. COUNT(*) когда нужен DISTINCT

-- неправильно: считает строки, не пользователей
SELECT COUNT(*) FROM events;

-- правильно
SELECT COUNT(DISTINCT user_id) FROM events;

2. DISTINCT внутри группировки

-- не то, что думаете
SELECT category, COUNT(DISTINCT *) FROM orders GROUP BY category;  -- invalid

-- правильно
SELECT category, COUNT(DISTINCT user_id) FROM orders GROUP BY category;

3. NULL в DISTINCT

COUNT DISTINCT игнорирует NULL. Если нужны NULL в счёте:

COUNT(DISTINCT COALESCE(col, 'missing'))

4. Смешивать COUNT и SUM

COUNT считает строки / non-null. SUM суммирует значения. Разные вещи.

Связанные темы

FAQ

COUNT DISTINCT медленный — что делать?

Approximate count (uniq в ClickHouse, APPROX_COUNT_DISTINCT в BigQuery/Snowflake). Или партиционирование.

Точный или приблизительный?

Для аналитики с миллионами записей ±1% обычно ок. Approximate в 10-100 раз быстрее.

DISTINCT нужен в group by?

Нет. GROUP BY уже группирует по уникальным значениям.

NULL считается в COUNT DISTINCT?

Нет, NULL игнорируется. Для включения — COALESCE.


Тренируйте SQL — откройте тренажёр с 1500+ вопросами для собесов.