> Напиши SQL запрос для вывода суммы продаж по каждому пользователю, учитывая, что у пользователя может не быть продаж (Go)
Уровень: middle · Роль: backend · Язык: Go · Категория: Технические вопросы
Компании: Ozon
Стек: Go
> Пример ответа
Короткий ответ
Используйте LEFT JOIN между таблицами пользователей и продаж с группировкой по пользователю. Для пользователей без продаж сумма будет NULL, поэтому оберните её в COALESCE(SUM(s.amount), 0). Запрос:
SQLSELECT u.id, u.name, COALESCE(SUM(s.amount), 0) AS total_salesFROM users uLEFT JOIN sales s ON u.id = s.user_idGROUP BY u.id, u.name;
Подробное объяснение
LEFT JOIN гарантирует, что все строки из левой таблицы (users) попадут в результат, даже если для них нет совпадений в правой (sales). Без него пользователи без продаж просто пропадут из выборки.
SUM(s.amount) для таких пользователей вернёт NULL, потому что нет строк для агрегации. COALESCE заменяет NULL на 0, что удобно для дальнейшей обработки.
Группировка по u.id, u.name обязательна - если сгруппировать только по id, в некоторых СУБД (например, MySQL с ONLY_FULL_GROUP_BY) запрос упадёт, а в PostgreSQL без строгого режима поведение будет недетерминированным.
Если в таблице продаж поле суммы называется иначе - замените amount на актуальное имя. Также можно использовать COUNT(s.id) для подсчёта количества продаж, но для суммы нужен именно SUM.
На практике
В реальных проектах часто добавляют фильтр по дате или статусу продажи. Например, только оплаченные заказы за последний месяц:
SQLWHERE s.status = 'paid' AND s.created_at >= NOW() - INTERVAL '1 month'
Но осторожно: если фильтр поставить в WHERE, LEFT JOIN превратится в INNER JOIN - пользователи без продаж исчезнут. Фильтры по правой таблице нужно переносить в условие JOIN:
SQLLEFT JOIN sales s ON u.id = s.user_id AND s.status = 'paid'
Также стоит подумать об индексах: sales(user_id) и, желательно, покрывающий индекс sales(user_id, amount) для ускорения агрегации.
Пример кода
Полный пример с фильтром по дате и сортировкой:
SQLSELECTu.id,u.name,COALESCE(SUM(s.amount), 0) AS total_salesFROM users uLEFT JOIN sales sON u.id = s.user_idAND s.created_at >= '2024-01-01'GROUP BY u.id, u.nameORDER BY total_sales DESC;
Как отвечать на собеседовании
Начните с ключевой идеи - LEFT JOIN + COALESCE. Затем объясните, почему нельзя использовать WHERE для фильтрации по таблице продаж. Если спросят про производительность - упомяните индексы и возможность предварительной агрегации в подзапросе:
SQLSELECT u.id, u.name, COALESCE(t.total, 0)FROM users uLEFT JOIN (SELECT user_id, SUM(amount) AS totalFROM salesGROUP BY user_id) t ON u.id = t.user_id;
Этот вариант может быть эффективнее при большом количестве продаж на пользователя, но обычно проще и понятнее первый.
Что проверяет интервьюер
- Понимание разницы между
INNER JOINиLEFT JOIN. - Знание поведения агрегатных функций с
NULL. - Умение корректно группировать и избегать ошибок с
ONLY_FULL_GROUP_BY. - Понимание, куда ставить условия фильтрации (в
WHEREvs вON). - Базовые навыки оптимизации (индексы, подзапросы).
Типичные ошибки
- Использование
INNER JOIN- теряются пользователи без продаж. - Забытый
COALESCE- в результатеNULL, а не0. - Фильтр по
s.amount > 0вWHERE- превращаетLEFT JOINвINNER JOIN. - Группировка только по
u.idбезu.name- ошибка в строгих режимах SQL. - Использование
SUM(*)илиSUM(u.id)- синтаксически неверно, нужен конкретный столбец. - Игнорирование индексов на
user_id- запрос будет медленным на больших таблицах.
> Похожие задачи по Go
Происходит ли копирование данных при передаче слайса и массива в функцию в Go
Расскажите про опыт оптимизации
Что такое система git и насколько хорошо вы с ней знакомы
Для чего используется MongoDB
> Похожие задачи по backend
Что такое MITM (man-in-the-middle) атака и как с ней бороться
Расскажите про опыт оптимизации
Что такое система git и насколько хорошо вы с ней знакомы
Для чего используется MongoDB
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью