@Etmac

Правильная ли декомпозиция базы данных?

Есть база данных для стадиона или еще для какого-нибудь мероприятия где есть возможность купить билет или просто забронировать место, пока что неважно. Цель в том чтобы для начала просто смотреть на то забронировано место или нет. Считается что есть сектор, ряд и естественно само место. Сначала я делал так, создавал таблицу sector , таблицу row и таблицу seat, которая хранила id сектора и ряда, а таблица user помимо полей для контактных данных , имела еще поля reserved(bool) и seat_id ( id таблицы seat с другими айдишниками) и еще поле для номера самого места. Но мне показалось что нужно и можно сделать намного проще, поэтому остановился на таком варианте:
User(table):
id int PK,
name varchar,
surname varchar
Seat(table)
id int PK,
row int,
number int,
sector enum('A', 'B', 'C')
Reservation(table)
id int PK,
row_id int FK,
seat_id int FK,
reserved bool

В общем, какой подход лучше всего первый или второй, если никакой то подскажите пожалуйста более правильный. Считается, что есть известное заранее кол-во мест, рядов и секторов. 5 рядов, 3 сектора, и 8 мест в 1 ряде. Заранее спасибо, за помощь.
  • Вопрос задан
  • 1786 просмотров
Решения вопроса 1
В целом, у вас всё достаточно просто. Я предложу вам вариант схемы, а вы сравните его со своей.
Попытаемся рассуждать в терминах множеств - это всегда полезно при проектировании реляционных БД, т.к. отношения - это множества.

Итак, у вас есть множество мест, доступных для бронирования. Будем считать, что БД обслуживает только ОДИН стадион. Сектор, ряд и номер места - это всё иерархические координаты места, однозначно это место определяющие. Таким образом, ключом для места является (sector, row, number).

Далее нам нужно решить, как мы будем представлять множество ВСЕХ мест. Вы указали, что количество мест известно заранее. По идее, в этом случае нам даже не нужно хранить множество этих мест, т.к. оно определяется правилом, однако рано или поздно вы столкнётесь с тем, что правило необходимо нарушить - например, какое-то из мест в данный момент оказалось непригодным для продажи билетов (сломали стул, отвалился порог, и т.д.). Поэтому ИМХО стоит всё-таки завести отношение "Место" для хранения множества ВСЕХ имеющихся мест, как вы собственно и сделали. Т.е. мы имеем первое отношение: Seat(sector, row, number).

Теперь мы хотим хранить множество забронированных мест. Т.к. событий на стадионе будет много, для каждого из событий мы будем иметь своё множество забронированных мест. Значит, в первичный ключ отношения "забронированное место" должен попасть первичный ключ события. Предположим, что первичный ключ события это id (т.к. других подробностей вы не указали). Остальные атрибуты отношения "забронированное место" должны ссылаться на одно из имеющихся мест, т.е. у нас должен быть внешний ключ в отношение "Место" (Seat). Итак, мы имеем второе отношение: ReservedSeat(event_id, sector, row, number). При проектировании реляционной БД очень важно четко понимать, что значит НАЛИЧИЕ или ОТСУТСТВИЕ записи в каждом из отношений. Наличие записи в отношении ReservedSeat говорит нам, что конкретное место забронировано на конкретное событие. Ни больше, ни меньше. Если некоторой записи в отношении ReservedSeat нет, значит конкретное место на конкретное событие все еще свободно.
И да, кажется мы забыли главное - а кем место-то занято? Нам нужен еще один атрибут, внешний ключ в отношение User. Добавим его:
ReservedSeat(event_id, sector, row, number, user_id)
. Важно, что этот атрибут не входит в первичный ключ, т.к. разные клиенты не могут забронировать одно и то же место (т.е. место всегда бронируется кем-то одним).

Собственно, всё. Отношение User переписываем как есть, т.к. вы не указали подробностей о том, что там хранить. Отношение Event я придумаю "с потолка", добавив туда только атрибут name помимо ключа id.

Итого (атрибуты, входящие в первичный ключ, выделены жирным):
User(id, name, surname);
Seat(sector, row, number);
Event(id, name);
ReservedSeat(event_id, sector, row, number, user_id); внешние ключи: event_id -> Event(id), user_id -> User(id); (sector, row, number) -> Seat(sector, row, number).

Надеюсь, процесс принятия решений описан достаточно понятно. Если остались вопросы - задавайте.
Ответ написан
Пригласить эксперта
Ответы на вопрос 3
я бы сделал так:
есть набор мест, которые как-то там характеризируются (пускай это будет сектор, ряд и место - на самом деле это неважно) и каждая такая запись имеет свою айдишку:
[Seat]
id
sector
row
number

на сколько я понимаю, покупаются/резервируются места на некоторое мероприятие (матч, концерт и т.д.). соответственно, должна быть таблица этих событий. дл простоты охарактеризируем ее только датой. нам и дата не нужна, но пускай себе будет
[Event]
id
datetime

резервирование или покупка места должна относиться к таблице Seat и к таблице Event:
[Reservation]
id
seat_id
event_id
// another fields

Seat к Reservation как one-to-many
Event к Reservation как one-to-many
пользователя можно втулить сюда же, но бы выделил связь с юзером в отдельны сущности: Ticket как билет и еще одну сущность как факт продажи билета некому юзеру TicketTransfer. но это уже зависит от вашего сервиса, который вы реализуете

я считаю, что так будет правильно
Ответ написан
Комментировать
alexey-m-ukolov
@alexey-m-ukolov Куратор тега MySQL
Я сам часто использую enum, но нужно понимать их ограничения/особенности и не лепить бездумно. Рекомендую почитать книгу SQL Antipatterns. Там есть неоднозначные советы, но она по крайней мере даст пищу для размышлений.
Наверное, всё же я сделал бы отдельные таблицы для секторов и рядов сектора, потому что сейчас считается, что столько-то секторов, рядов и мест, а потом вдруг станет считаться иначе. А в таблице мест ссылался бы только на нужный ряд сектора.

То, что вы вынесли брони из пользователей совершенно правильно, не место им там.

А вот таблица броней странная:
  • Зачем там булево поле, если это и так брони? Может быть незабронированная бронь?
  • Где ссылка на забронировавшего пользователя? Она может быть в таблице броней или в отдельной таблице, но без неё не обойтись.
  • Зачем ссылаться и на место и на ряд, если место уже привязано к ряду? Причём в таблице мест ряд - просто int, а в бронях ещё и внешний ключ.
  • По хорошему, места резервируются на какие-то события, а не навечно, поэтому для полноты картины необходимо их добавить.
Ответ написан
MetaAbstract
@MetaAbstract
Архитектор информационных систем и баз данных. Ful
Дело то во времени происходит. Или Вы один раз на каждое место планируете резерв ставить?
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы