Примеры / Библиотечная система
Библиотечная система
Схема библиотеки держится на одном разделении. Книга – это название в каталоге, а экземпляр – физический том на полке. Читатели берут экземпляры, а не книги, а бронь ставится на книгу. Авторы подключаются через промежуточную таблицу, потому что у книги бывает несколько авторов.
Ниже ErdDocs рисует эту схему по её SQL, со всеми таблицами и связями. На вкладке «Словарь данных» – справочник для печати, целиком и бесплатно на этой странице. SQL ещё ниже.
Наведите на таблицу, чтобы подсветить её связи, кликните, чтобы закрепить.
Зачем нужна каждая таблица
- books / copiesЗапись каталога и экземпляр со штрихкодом. Большинство задач про библиотеку сводятся к этому разделению.
- authors / book_authorsМногие ко многим: книга с двумя авторами сразу ломает прямой author_id.
- membersЛюди пользуются номером читательского билета, card_no.
- loansВыдача привязана к экземпляру, потому что нужно знать, какой именно том на руках и просрочен.
- reservationsБронь привязана к книге. Читателю нужно название, и неважно, какой экземпляр вернётся первым.
Проектные решения, которые стоит взять
- Выдачи ссылаются на экземпляры, брони – на книги. Смешать эти два уровня – классическая ошибка. Тогда схема не ответит ни на вопрос «у кого этот том», ни на вопрос «кого уведомить следующим».
- returned_on допускает NULL. Это заменяет флаг «возвращён», а дата хранится в той же колонке.
- ISBN уникален, но допускает NULL. Старые и внутренние издания без ISBN существуют, и с NOT NULL реальные данные нарушали бы ограничение.
SQL
Диалект PostgreSQL. Этот SQL уже вставлен в инструмент выше, его можно перенести в свою базу или в миграцию и свободно копировать.
CREATE TABLE authors (
id bigserial PRIMARY KEY,
full_name varchar(120) NOT NULL
);
CREATE TABLE books (
id bigserial PRIMARY KEY,
isbn varchar(17) UNIQUE,
title varchar(240) NOT NULL,
published smallint
);
COMMENT ON COLUMN books.isbn IS 'NULL для изданий до ISBN и внутренних материалов';
CREATE TABLE book_authors (
book_id bigint NOT NULL REFERENCES books(id),
author_id bigint NOT NULL REFERENCES authors(id),
PRIMARY KEY (book_id, author_id)
);
CREATE TABLE copies (
id bigserial PRIMARY KEY,
book_id bigint NOT NULL REFERENCES books(id),
barcode varchar(20) NOT NULL UNIQUE,
shelf varchar(20),
condition varchar(20) NOT NULL DEFAULT 'good'
);
COMMENT ON TABLE copies IS 'Физический том; у популярной книги много экземпляров';
CREATE TABLE members (
id bigserial PRIMARY KEY,
card_no varchar(20) NOT NULL UNIQUE,
full_name varchar(120) NOT NULL,
joined_on date NOT NULL
);
CREATE TABLE loans (
id bigserial PRIMARY KEY,
copy_id bigint NOT NULL REFERENCES copies(id),
member_id bigint NOT NULL REFERENCES members(id),
loaned_on date NOT NULL,
due_on date NOT NULL,
returned_on date
);
COMMENT ON COLUMN loans.returned_on IS 'NULL, пока экземпляр на руках';
CREATE TABLE reservations (
id bigserial PRIMARY KEY,
book_id bigint NOT NULL REFERENCES books(id),
member_id bigint NOT NULL REFERENCES members(id),
placed_at timestamptz NOT NULL DEFAULT now(),
fulfilled boolean NOT NULL DEFAULT false
);
COMMENT ON TABLE reservations IS 'Бронь на КНИГУ: её закроет любой вернувшийся экземпляр';Частые вопросы
Куда положить штрафы?
В таблицу fines со ссылкой на loans, ведь штраф берут за просроченную выдачу. В ней сумма, дата начисления и дата оплаты. Для учебного проекта подойдёт и расчёт на лету по сроку возврата. Главное – объяснить выбор.
Как изменится схема с электронными книгами?
У электронной книги нет экземпляров, и выдача становится вопросом лицензий. Можно завести таблицу licenses рядом с copies, где указано число одновременных выдач. А можно выдавать прямо книгу и проверять число выдач в приложении.