> Напиши SQL запрос для выбора всех чатов пользователя с именем Вася (Go)
Уровень: middle · Роль: backend · Язык: Go · Категория: Технические вопросы
Компании: Ozon
Стек: Go
> Пример ответа
Короткий ответ
SQLSELECT c.*FROM chats cJOIN chat_participants cp ON cp.chat_id = c.idJOIN users u ON u.id = cp.user_idWHERE u.name = 'Вася';
Этот запрос выбирает все чаты, где Вася является участником. Используются три таблицы: users, chat_participants (связка many-to-many) и chats. Если схема другая - например, поле user_id прямо в chats - запрос упрощается до SELECT * FROM chats WHERE user_id = (SELECT id FROM users WHERE name = 'Вася').
Подробное объяснение
В типичной схеме мессенджера чаты и пользователи связаны через промежуточную таблицу chat_participants, так как отношение many-to-many: один пользователь может быть во многих чатах, один чат - содержать многих пользователей.
Запрос работает так:
JOIN chat_participantsсвязывает чаты с записями об участии.JOIN usersсвязывает участников с пользователями.WHERE u.name = 'Вася'фильтрует только те строки, где имя совпадает.
Важно: если имя не уникально, запрос вернёт чаты всех пользователей с именем Вася. Для точности лучше фильтровать по id пользователя, а не по имени.
На практике
В реальном коде на Go с использованием database/sql запрос обычно параметризуется:
GOrows, err := db.Query(`SELECT c.id, c.title, c.created_atFROM chats cJOIN chat_participants cp ON cp.chat_id = c.idJOIN users u ON u.id = cp.user_idWHERE u.name = $1`, "Вася")
Имя пользователя передаётся как параметр - это защищает от SQL-инъекций. Также стоит добавить индекс на users.name и chat_participants.user_id для производительности.
Если нужно получить чаты с количеством участников или последним сообщением - добавляются LEFT JOIN и агрегатные функции.
Пример кода
Полный пример на Go:
GOtype Chat struct {ID int64Title stringCreatedAt time.Time}func GetChatsByUserName(db *sql.DB, name string) ([]Chat, error) {rows, err := db.Query(`SELECT c.id, c.title, c.created_atFROM chats cJOIN chat_participants cp ON cp.chat_id = c.idJOIN users u ON u.id = cp.user_idWHERE u.name = $1`, name)if err != nil {return nil, err}defer rows.Close()var chats []Chatfor rows.Next() {var ch Chatif err := rows.Scan(&ch.ID, &ch.Title, &ch.CreatedAt); err != nil {return nil, err}chats = append(chats, ch)}return chats, rows.Err()}
Как отвечать на собеседовании
Начни с уточнения схемы: спроси, есть ли таблица chat_participants или связь напрямую. Это покажет понимание предметной области. Затем предложи запрос с JOIN и объясни каждый шаг. Упомяни про индексы и параметризацию. Если спросят про производительность - скажи, что при больших объёмах данных стоит фильтровать по user_id, а не по имени. Если спросят про альтернативы - упомяни EXISTS вместо JOIN для избежания дубликатов.
Что проверяет интервьюер
- Понимание SQL JOIN и связей many-to-many.
- Умение проектировать запрос под конкретную схему.
- Внимание к деталям: уникальность имени, индексы, параметризация.
- Способность объяснять выбор подхода и trade-off.
Типичные ошибки
- Забыть про промежуточную таблицу и написать
WHERE user_id = ...напрямую. - Не учитывать, что имя может быть неуникальным - вернутся лишние чаты.
- Использовать
SELECT *без необходимости - лучше перечислить нужные колонки. - Не параметризовать запрос - риск SQL-инъекции.
- Игнорировать индексы на колонках, участвующих в JOIN и WHERE.
> Похожие задачи по Go
Как работает двусвязный список в LRU кэше
Почему использовать UUID вместо int для идентификаторов
Как шардировать таблицы пользователей, чатов и сообщений
Какие плюсы и минусы шардирования по сообщениям и по пользователям
> Похожие задачи по backend
Как работает двусвязный список в LRU кэше
Почему использовать UUID вместо int для идентификаторов
Как шардировать таблицы пользователей, чатов и сообщений
Какие плюсы и минусы шардирования по сообщениям и по пользователям
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью