> Какая связь между таблицами людей и адресов в базе данных (Go)
Уровень: senior · Роль: backend · Язык: Go · Категория: Технические вопросы
Компании: Wildberries
Стек: Go
> Пример ответа
Короткий ответ
Связь между таблицами людей и адресов - это отношение "один ко многим" (one-to-many): один человек может иметь несколько адресов (регистрация, фактический, рабочий), но каждый адрес принадлежит одному человеку. В реляционной модели это реализуется через внешний ключ (foreign key) в таблице адресов, ссылающийся на первичный ключ таблицы людей. Альтернативный вариант - связь "многие ко многим" через промежуточную таблицу, если адрес может быть общим для нескольких людей (например, семья).
Подробное объяснение
В классической схеме:
- Таблица
peopleсодержит первичный ключid. - Таблица
addressesсодержит колонкуperson_idкак внешний ключ, ссылающийся наpeople.id.
Это нормализованная модель (3NF), где адрес - зависимая сущность. Каждый адрес хранится отдельно, что позволяет хранить историю адресов, добавлять типы (registration, actual, delivery) и избегать дублирования данных.
Если бизнес-требование допускает, что один адрес используется несколькими людьми (например, члены семьи), используется связь many-to-many через таблицу person_addresses с полями person_id и address_id. Это даёт гибкость, но усложняет запросы и требует дополнительных проверок уникальности.
Выбор между one-to-many и many-to-many зависит от домена. Для большинства систем (CRM, e-commerce) one-to-many достаточно и проще в поддержке. В Go-проектах с ORM (GORM, sqlx) эта связь описывается через HasMany или ManyToMany соответственно.
На практике
При проектировании схемы важно определить:
- Может ли человек иметь несколько адресов одновременно? Если да - one-to-many.
- Может ли один адрес быть привязан к нескольким людям? Если да - many-to-many.
- Нужна ли история адресов? Тогда добавляем
valid_from/valid_toи не удаляем записи физически.
В Go-приложениях слой доступа к данным обычно скрывает join-запросы за репозиториями. Например, метод GetUserWithAddresses(ctx, userID) выполняет два запроса (или один с LEFT JOIN) и собирает структуру с вложенным слайсом адресов.
Также стоит подумать об индексах: внешний ключ addresses.person_id должен быть проиндексирован для быстрых выборок по человеку. При удалении человека нужно решить политику - ON DELETE CASCADE или ON DELETE SET NULL (если адрес может существовать без владельца).
Пример кода
GOtype Person struct {ID int64 `db:"id"`Name string `db:"name"`Addresses []Address `db:"-"`}type Address struct {ID int64 `db:"id"`PersonID int64 `db:"person_id"`Street string `db:"street"`City string `db:"city"`}// Получение человека с адресами (два запроса)func GetPersonWithAddresses(ctx context.Context, db *sql.DB, personID int64) (*Person, error) {var p Personerr := db.GetContext(ctx, &p, "SELECT id, name FROM people WHERE id = $1", personID)if err != nil {return nil, err}err = db.SelectContext(ctx, &p.Addresses,"SELECT id, person_id, street, city FROM addresses WHERE person_id = $1", personID)if err != nil {return nil, err}return &p, nil}
Как отвечать на собеседовании
Начните с прямого ответа: "обычно это one-to-many". Затем уточните, что выбор зависит от требований, и приведите пример, когда нужна many-to-many. Покажите понимание нормализации и внешних ключей. Упомяните практические детали: индексы, каскадное удаление, загрузка связанных данных в Go. Если интервьюер спрашивает про ORM - опишите, как маппятся HasMany и ManyToMany. Не углубляйтесь в излишние детали, пока не попросят.
Что проверяет интервьюер
- Понимание реляционной модели и типов связей.
- Умение обосновать выбор схемы под бизнес-требования.
- Знание практических аспектов: внешние ключи, индексы, join-запросы.
- Опыт работы с Go и типичными паттернами доступа к БД (repository, sqlx, GORM).
- Способность рассуждать о trade-off между нормализацией и производительностью.
Типичные ошибки
- Ответ "многие ко многим" без обоснования - в большинстве случаев это неверно.
- Игнорирование вопроса об истории адресов или типах адресов.
- Предложение хранить адреса в JSON-поле - это антипаттерн для нормализованной схемы.
- Забывают про индексы на внешних ключах.
- В Go-коде делают N+1 запросов вместо одного join или двух структурированных запросов.
> Похожие задачи по Go
Как подбирать тайм-ауты для backend взаимодействия, например HTTP запросов
Какие требования по шифрованию данных существуют
Кто пишет миграции в проекте
Как масштабировать Kafka при высокой нагрузке
> Похожие задачи по backend
Как подбирать тайм-ауты для backend взаимодействия, например HTTP запросов
Какие требования по шифрованию данных существуют
Кто пишет миграции в проекте
Как масштабировать Kafka при высокой нагрузке
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью