COUNT(*) против COUNT(col) против COUNT(DISTINCT)

Три разных COUNT, которые путают на собеседованиях: COUNT(*), COUNT(col) и COUNT(DISTINCT col).

COUNT(*) считает строки, COUNT(col)ненулевые значения колонки, COUNT(DISTINCT col)уникальные ненулевые значения.

В чём разница

Ключевое отличие — отношение к NULL. COUNT(*) считает все строки подряд. COUNT(col) пропускает строки, где col равен NULL. COUNT(DISTINCT col) вдобавок схлопывает одинаковые значения.

CREATE TABLE feedback (
    id     INTEGER PRIMARY KEY,
    user   TEXT,
    rating INTEGER   -- может быть NULL, если оценку не поставили
);
INSERT INTO feedback (user, rating) VALUES
    ('Аня',   5),
    ('Борис', 5),
    ('Вера',  NULL),   -- нет оценки
    ('Гена',  3),
    ('Дина',  NULL);   -- нет оценки

SELECT
    COUNT(*)                AS vsego_strok,        -- 5
    COUNT(rating)           AS s_ocenkoy,          -- 3 (NULL не считаются)
    COUNT(DISTINCT rating)  AS unikalnyh_ocenok    -- 2 (значения 5 и 3)
FROM feedback;

Вывод:

5|3|2

Всего 5 строк. С оценкой — только 3 (две NULL отброшены). Уникальных оценок — 2: значения 5 и 3 (повтор пятёрки схлопнут, NULL не считаются).

Практический вывод

Когда нужно «сколько всего записей» — берите COUNT(*). Когда «у скольких заполнено поле» — COUNT(col). Когда «сколько разных значений» — COUNT(DISTINCT col). Путаница тут приводит к неверной аналитике.

COUNT в связке с LEFT JOIN

Тонкость: после LEFT JOIN у клиентов без заказов правые колонки — NULL. Если посчитать COUNT(*), такой клиент получит 1 (строка-то есть). А правильно — COUNT(o.id), который даст 0, потому что o.id там NULL.

CREATE TABLE customers (id INTEGER PRIMARY KEY, name TEXT);
INSERT INTO customers (id, name) VALUES (1,'Аня'), (2,'Вера');

CREATE TABLE orders (id INTEGER PRIMARY KEY, customer_id INTEGER);
INSERT INTO orders (customer_id) VALUES (1), (1);

SELECT c.name,
       COUNT(*)     AS count_zvezda,   -- у Веры будет 1 (ловушка!)
       COUNT(o.id)  AS count_zakazov   -- у Веры честный 0
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
GROUP BY c.name
ORDER BY c.name;

Вывод:

Аня|2|2
Вера|1|0

У Веры count_zvezda = 1 (строка от LEFT JOIN есть), но count_zakazov = 0 — заказов реально нет. После LEFT JOIN всегда считайте COUNT(колонка_правой_таблицы), а не COUNT(*).

Итог

  • COUNT(*) — все строки; COUNT(col) — без NULL; COUNT(DISTINCT col) — уникальные без NULL.
  • После LEFT JOIN считайте COUNT(колонка_правой_таблицы), иначе клиенты без пар получат лишнюю единицу.
  • Выбор COUNT влияет на цифры в отчётах — не путайте смыслы.
Проверьте себя
1. Чем COUNT(col) отличается от COUNT(*)?
AНичем
BCOUNT(col) не считает строки, где col равен NULL
CCOUNT(col) считает только уникальные
DCOUNT(*) медленнее
2. Что вернёт COUNT(DISTINCT rating), если значения 5, 5, NULL, 3, NULL?
A5
B3
C2
D4
3. Почему после LEFT JOIN лучше COUNT(o.id), а не COUNT(*)?
ACOUNT(*) запрещён с JOIN
BКлиент без пары даёт строку с NULL — COUNT(*) посчитает её за 1, а COUNT(o.id) даст 0
CТак быстрее
DРазницы нет
Поддержать проект