Как связать книги и пользователей, если одна книга не может быть выдана нескольким пользователям одновременно
Что проверяют
Умеете ли вы превратить бизнес-правило в модель данных и ограничение, которое держит сама база. Уточнение следом обычно такое: что будет, если два библиотекаря одновременно выдают один экземпляр, и как хранить историю выдач.
Как отвечать
Сначала уточните предметную область: книга как издание и конкретный экземпляр на полке это разные сущности. Выдают экземпляр, а у одного издания их может быть десять. Отсюда модель:
- users: читатели;
- books: издания, название, автор, ISBN;
- copies: экземпляры, ссылка на
book_id, инвентарный номер; - loans: выдачи,
user_id,copy_id,issued_at,due_at,returned_at.
Связь пользователя и экземпляра за всё время многие ко многим, поэтому нужна таблица выдач. Правило «не больше одной активной выдачи на экземпляр» ставится ограничением: в PostgreSQL это частичный уникальный индекс CREATE UNIQUE INDEX ON loans (copy_id) WHERE returned_at IS NULL. Вторая выдача не запишется, даже если проверка в приложении пропустила её из-за гонки. В MySQL частичных индексов нет, там заводят генерируемую колонку, равную copy_id у активной выдачи и NULL у закрытой, и вешают на неё уникальный индекс: несколько NULL он допускает.
Если появятся брони на будущие даты, условие становится интервальным: в PostgreSQL для этого есть exclusion constraint по диапазону дат, он запрещает пересечение интервалов одного экземпляра.
На собеседовании полезно нарисовать ER-диаграмму с кратностями и отдельно проговорить сценарии: выдача, возврат, продление, просрочка.
Где спотыкаются
Кладут user_id прямо в таблицу книг. Текущий держатель есть, а истории выдач и различия между экземплярами нет.
Заводят флаг is_available рядом с таблицей выдач. Получаются два источника правды, которые расходятся после первого сбоя между двумя UPDATE.
Проверяют доступность только в коде: «прочитал, свободна, записал». Между чтением и записью успевает пройти второй запрос, и без ограничения в базе книга уходит двоим.