Корректность запросов

Гранулярность результата и уникальность ключа

Содержание курса

Проверка не нашла повторов, а отчёт неверен

Успешная проверка ключа через HAVING COUNT(*) > 1 показывает только отсутствие одинаковых ключей в текущей выборке. Сам отчёт при этом может быть ошибочным сразу по нескольким причинам.

Первая причина — неверный состав столбцов группировки. Если требовался отчёт по клиентам, но в GROUP BY добавили дату заказа, каждый день разобьёт клиента на отдельную строку. Проверка по паре (client_id, order_date) не покажет дублей, но гранулярность «один клиент — одна строка» будет нарушена.

Вторая причина — группировка по неуникальному признаку вместо идентификатора. Посмотрим отдельный контрпример: не меняя базовые таблицы, подменим имя клиента 2 с «Борис» на «Анна» через CTE. Если сгруппировать заказы только по имени:

WITH clients_cte(client_id, client_name) AS (
    VALUES (1, 'Анна'), (2, 'Анна'), (3, 'Вера')
)
SELECT c.client_name, SUM(o.total) AS total_spent
FROM clients_cte c
JOIN orders o ON o.client_id = c.client_id
GROUP BY c.client_name
ORDER BY c.client_name;

Результат объединит двух разных людей:

client_name total_spent
Анна 4500

Проверка по client_name не найдёт повторов, но расходы Анны (3000) и второго клиента (1500) сложились в одну строку.

Если же вернуть в группировку ключ client_id, но скрыть его из итогового SELECT:

WITH clients_cte(client_id, client_name) AS (
    VALUES (1, 'Анна'), (2, 'Анна'), (3, 'Вера')
)
SELECT c.client_name, SUM(o.total) AS total_spent
FROM clients_cte c
JOIN orders o ON o.client_id = c.client_id
GROUP BY c.client_id, c.client_name
ORDER BY c.client_id;

Запрос вернёт две строки:

client_name total_spent
Анна 3000
Анна 1500

Сам по себе пропуск столбца в блоке SELECT группы не объединяет: число строк определил GROUP BY c.client_id, c.client_name.

Третья причина — отсутствие GROUP BY при наличии агрегатной функции. Запрос:

SELECT client_id, SUM(total) AS total_spent
FROM orders;

возвращает ровно одну строку:

client_id total_spent
1 4500

Функция SUM(total) сложила суммы всех трёх заказов компании (4500), а неагрегированный столбец client_id вернул произвольное значение (в данном запуске — 1, но SQLite не гарантирует выбор конкретной строки). Проверка ключа по этой единственной строке покажет ноль дублей, хотя отчёт по клиентам не построен.

Четвёртая причина — неполнота данных при изменении требований. Для исходной задачи «клиенты с покупками» отсутствие Веры (client_id = 3) корректно: у неё нет заказов, и INNER JOIN отсёк её ожидаемо. Но если бизнес-требование меняется на «отчёт по всем клиентам компании», этот же результат становится неверным из-за потери данных:

SELECT c.client_id
FROM clients c
WHERE NOT EXISTS (
    SELECT 1 FROM orders o WHERE o.client_id = c.client_id
)
ORDER BY c.client_id;

Запрос находит Веру (3). Никакая проверка уникальности ключа в готовом отчёте не выявит её пропажу, потому что считает дубли только среди тех строк, которые уже дошли до результата.

Начать заново?

Помощник начнёт новый диалог по этой задаче. Текущая переписка останется в истории до конца срока хранения.

О переписке

Можно приложить до трёх фото PNG, JPEG или WebP по 5 МБ.

Переписка и фото хранятся 90 дней после последнего принятого сообщения. Преподаватель получит доступ к диалогу, когда вы попросите его помочь.

Проверка решения остаётся отдельным действием.