Концептуальное проектирование: модель «сущность — связь»
В темах 2–6 учебная база «Книжный узел» была дана готовой: таблицы, ключи и ограничения целостности существовали заранее, и задача состояла в том, чтобы понимать их смысл и писать к ним запросы. Однако схема базы данных не возникает сама собой. Кто-то решил, что автор — это отдельная таблица, а не текстовое поле книги, что у книги может не быть издательства, что покупатель оставляет не более одного отзыва на книгу, а цена в позиции заказа хранится отдельно от цены в каталоге. Каждое такое решение отражает представление о предметной области, и ошибка в нём обходится дорого: исправить схему, в которой уже лежат миллионы строк и с которой работают десятки программ, многократно сложнее, чем исправить рисунок на этапе обсуждения.
Раздел II курса посвящён проектированию — переходу от словесного описания предметной области к схеме базы данных. Тема открывает раздел и отвечает на первый вопрос: как описать предметную область точно, но ещё без таблиц, столбцов и типов данных. Для этого служит модель «сущность — связь» (англ. entity-relationship model, ER-модель), предложенная П. Ченом в 1976 году 1. Её словарь — сущности, атрибуты, связи, кардинальности — достаточно прост, чтобы обсуждать модель с заказчиком, и достаточно строг, чтобы почти механически превратить её в реляционную схему.
Изложение идёт от этапов проектирования к основным и более тонким конструкциям ER-модели (слабые сущности, атрибуты связей, тернарные связи), затем к нотациям — Чена, «вороньи лапки», UML, IDEF1X — и к обобщению со способами хранить иерархию типов в таблицах. Последний раздел формулирует правила преобразования ER-модели в реляционную схему, показывает обратный путь — восстановление модели учебной базы по системному каталогу — и разбирает типичные ошибки моделирования. Вопрос о том, какие из полученных таблиц избыточны и как это строго проверить, отложен до темы 8 (функциональные зависимости и нормализация), а выбор типов данных и физическое размещение — до темы 9.
Этапы проектирования базы данных
Требования, концептуальная, логическая и физическая модели
Проектирование базы данных принято делить на четыре этапа, каждый из которых отвечает на свой вопрос и даёт свой результат 23.
| Этап | Вопрос | Результат | В курсе |
|---|---|---|---|
| Анализ требований | Что должна хранить и уметь система? | словарь предметной области, бизнес-правила, перечень запросов и операций | эта тема |
| Концептуальное проектирование | Какие объекты и связи существуют в предметной области? | ER-модель (диаграмма и описание) | эта тема |
| Логическое проектирование | Как представить модель в выбранной модели данных? | реляционная схема: отношения, ключи, внешние ключи, нормальные формы | эта тема и тема 8 |
| Физическое проектирование | Как эффективно хранить и находить данные в конкретной СУБД? | типы данных, индексы, секционирование, параметры хранения | темы 9 и 10 |
Концептуальная модель (англ. conceptual model) описывает предметную область независимо от модели данных и СУБД: в ней нет таблиц, внешних ключей и типов integer, есть «книга», «автор» и утверждение «у книги может быть несколько авторов». Логическая модель (англ. logical model) — та же информация, выраженная в терминах конкретной модели данных; для реляционной СУБД это набор схем отношений с ключами и ограничениями (тема 2). Физическая модель (англ. physical model) добавляет то, что нужно для производительности и эксплуатации: конкретные типы, индексы, размещение по табличным пространствам.
Разделение этапов отделяет вопросы разной природы. Утверждение «покупатель может оставить не более одного отзыва на книгу» — факт о предметной области, его подтверждает заказчик; утверждение «столбец email имеет тип text» — инженерное решение, о котором заказчику знать не нужно.
Следует оговорить терминологическую тонкость. В трёхуровневой архитектуре ANSI/SPARC (тема 1) «концептуальным уровнем» называется общая логическая схема всей базы данных — то, что в таблице выше названо логической моделью. В литературе по проектированию слово «концептуальный» обычно обозначает более ранний, не зависящий от модели данных уровень 3. В этой теме оно используется во втором смысле.
На практике этапы повторяются итеративно: построение ER-модели выявляет пробелы в требованиях, логическое проектирование — избыточность модели, физическое — слишком дорогие запросы. Но порядок вопросов сохраняется: сначала смысл, затем структура, затем эффективность.
Бизнес-правила и словарь предметной области
Исходный материал проектирования — требования: интервью с будущими пользователями, существующие документы (бланки заказов, отчёты, выгрузки из старых систем), описания процессов. Результат анализа требований удобно фиксировать в двух формах.
Словарь предметной области (англ. glossary) даёт понятиям точные определения. «Заказ» кажется очевидным, пока не выясняется, что для отдела продаж это то, что оформил покупатель, а для склада — одна посылка; если заказ может уйти двумя посылками, в модели это две разные сущности.
Бизнес-правило (англ. business rule) — утверждение, ограничивающее допустимые состояния данных или действия над ними 4. Для «Книжного узла» из описания магазина извлекаются, например, такие правила:
- каждая книга относится ровно к одной рубрике; рубрики образуют дерево;
- у книги может не быть издательства (самиздат, издания без выходных данных), а издательство может выпускать много книг;
- у книги может быть несколько авторов, и их порядок на обложке важен;
- покупатель может оставить не более одного отзыва на книгу, оценка — от 1 до 5;
- цена в заказе — это цена на момент оформления; последующее изменение цены в каталоге не меняет уже оформленные заказы;
- заказ отгружается с одного склада.
Каждое правило станет элементом структуры (таблицей, внешним ключом), ограничением (NOT NULL, UNIQUE, CHECK) или, если декларативно его не выразить, триггером либо проверкой в приложении (тема 14). Например, последнее правило превратилось в столбец orders.warehouse_id, правило об отзывах — в ограничение UNIQUE (book_id, customer_id) таблицы review, а правило о цене — в столбец order_item.price, хранящийся отдельно от book.price.
Кандидатов в элементы модели подсказывает грамматическая эвристика: существительные — сущности или атрибуты, глаголы — связи, слова «каждый», «может», «не более одного» — кардинальности 3. Это лишь подсказка: «город» может оказаться и атрибутом, и сущностью (см. раздел о типичных ошибках).
Зачем нужен отдельный концептуальный уровень
У отдельного концептуального уровня три преимущества, которые растут с размером системы. Во-первых, он служит общим языком с заказчиком: диаграмма из десятка прямоугольников читается за минуты и сразу вызывает правильные вопросы («почему у отзыва нет связи с заказом — можно ли оценить некупленную книгу?»), тогда как текст DDL такого обсуждения не порождает. Во-вторых, он отделяет решения о смысле от решений о реализации: одна ER-модель может быть реализована реляционной схемой, документной базой (тема 16) или классами программы. В-третьих, правила преобразования ER-модели в таблицы почти механические, а получаемая схема при аккуратном моделировании сразу оказывается близкой к третьей нормальной форме (тема 8) 11, так что многие ошибки обнаруживаются ещё на рисунке.
Сущности, атрибуты и связи
Сущность и экземпляр сущности
Сущность (англ. entity) — это вещь или понятие предметной области, которое можно отличить от других и о котором нужно хранить сведения: конкретная книга «Пикник на обочине», конкретный покупатель, конкретный заказ № 5028 1. Множество однотипных сущностей, описываемых одинаковым набором свойств, называется типом сущности (англ. entity type) или множеством сущностей (англ. entity set): «Книга», «Покупатель», «Заказ». Отдельная сущность этого множества называется экземпляром (англ. instance).
На практике слово «сущность» почти всегда употребляют в смысле «тип сущности», а для отдельных объектов говорят «экземпляр»; в теме принято то же соглашение. Имена сущностей — существительные в единственном числе («Книга», а не «Книги»): диаграмма описывает, каким бывает один экземпляр и с чем он связан.
Сущность выделяют, если у понятия есть собственная идентичность и свойства, которые нужно хранить. Издательство — сущность: у него есть название и город, и при переименовании издательства все его книги должны «узнать» новое название. Переплёт книги — не сущность, а свойство книги: о самом переплёте хранить нечего.
Атрибуты и их виды
Атрибут (англ. attribute) — свойство сущности, значения которого хранятся для каждого экземпляра: название и год издания книги, адрес электронной почты покупателя. Множество допустимых значений атрибута называется доменом (англ. domain), как и в реляционной модели (тема 2). Чен различает атрибуты по нескольким независимым признакам 13:
- простой (англ. simple, atomic) атрибут не делится на части, имеющие самостоятельный смысл (год издания); составной (англ. composite) состоит из частей: «ФИО» из фамилии, имени и отчества, «адрес» из города, улицы и дома. Составной атрибут моделируют, если части нужны по отдельности (поиск по городу, сортировка по фамилии);
- однозначный (англ. single-valued) атрибут имеет у экземпляра одно значение (ISBN книги); многозначный (англ. multivalued) — несколько (телефоны издательства, ключевые слова книги);
- хранимый (англ. stored) атрибут вводится в базу; производный (англ. derived) вычисляется из других данных: число отзывов о книге, сумма заказа, возраст автора по году рождения;
- атрибут может быть необязательным: значение у части экземпляров отсутствует или неприменимо (город покупателя, год рождения автора). В реляционной схеме это станет столбцом, допускающим
NULL(тема 2).
Деление относительно: для магазина адрес — одна строка на посылке, для службы доставки — составной атрибут, разбираемый на город и улицу. Решение определяется тем, какие запросы будут выполняться, — ещё одна причина начинать проектирование с перечня запросов.
Особое место занимает ключ (англ. key) сущности — атрибут или набор атрибутов, значение которых однозначно идентифицирует экземпляр: ISBN книги, адрес электронной почты покупателя. Понятия суперключа, потенциального, первичного, естественного и суррогатного ключа были введены в теме 2; на концептуальном уровне важно выявить естественные ключи предметной области (ISBN, ИНН организации, номер паспорта) и правила их уникальности, а решение о суррогатных ключах integer GENERATED ... AS IDENTITY относится к логическому проектированию. В «Книжном узле» ISBN объявлен уникальным, но первичным ключом выбран суррогатный book_id: у части изданий ISBN может отсутствовать, а ISBN, присвоенный по ошибке, придётся исправлять.
Связи, их степень и роли
Связь (англ. relationship) — ассоциация между экземплярами сущностей: «автор написал книгу», «покупатель оформил заказ», «книга относится к рубрике» 1. Как и для сущностей, различают тип связи (множество однотипных ассоциаций) и экземпляр связи (конкретную пару «Аркадий Стругацкий — Пикник на обочине»). Формально тип связи между сущностями E1, …, En — это отношение, то есть подмножество декартова произведения E1 × … × En; связь — математическое отношение на множествах сущностей, и именно эта аналогия делает переход к реляционной модели естественным.
Степень связи (англ. degree) — число участвующих в ней сущностей. Подавляющее большинство связей бинарные (степень 2). Унарная, или рекурсивная, связь (англ. recursive relationship) соединяет сущность саму с собой: рубрика вложена в родительскую рубрику, сотрудник подчиняется руководителю. Связи степени 3 и выше (тернарные, n-арные) встречаются реже и требуют осторожности — им посвящён следующий раздел.
В рекурсивной связи один и тот же тип сущности участвует дважды, и, чтобы различать участников, им назначают роли (англ. role names): в связи «вложена в» рубрика выступает в ролях «потомок» и «родитель». В учебной базе эта связь реализована столбцом category.parent_id, ссылающимся на ту же таблицу; рубрики верхнего уровня имеют parent_id, равный NULL:
SELECT c.name AS category, p.name AS parent
FROM bookstore.category c
LEFT JOIN bookstore.category p ON p.category_id = c.parent_id
WHERE c.category_id IN (1, 13, 21)
ORDER BY c.category_id;
category | parent
---------------------------+-------------------------
Художественная литература |
Базы данных | Компьютерная литература
PostgreSQL | Базы данных
(3 rows)
Обход дерева на произвольную глубину — задача рекурсивных запросов (тема 6). На концептуальном уровне фиксируется, что у рубрики не более одного родителя и циклов нет; последнее правило внешним ключом не выражается и требует триггера или проверки в приложении.
Кардинальность и обязательность участия
Самое содержательное в связи — её ограничения. Кардинальность (англ. cardinality ratio), или максимальное число участий, указывает, со сколькими экземплярами другой сущности может быть связан один экземпляр 3. Для бинарных связей различают три случая:
- «один к одному» (1:1): экземпляр каждой стороны связан не более чем с одним экземпляром другой; пример — «покупатель — карта лояльности», если у покупателя бывает не более одной карты, а карта принадлежит одному покупателю;
- «один ко многим» (1:N): издательство выпускает много книг, но у книги не более одного издательства; покупатель оформляет много заказов, у заказа один покупатель;
- «многие ко многим» (M:N): у книги может быть несколько авторов, автор может написать несколько книг.
Обязательность участия (англ. participation constraint), или минимальное число участий, указывает, должен ли каждый экземпляр сущности участвовать в связи. Участие полное (англ. total, обязательное), если каждый экземпляр обязан участвовать: каждый заказ оформлен каким-то покупателем. Участие частичное (англ. partial, необязательное), если нет: покупатель может зарегистрироваться и не сделать ни одного заказа.
Оба ограничения удобно записывать парой (min, max) для каждой стороны связи: участие заказа в связи «оформил» — (1, 1), участие покупателя — (0, N). Такая запись точнее, чем «1:N», потому что различает, например, «у книги ноль или одно издательство» (0, 1) и «у книги ровно одно издательство» (1, 1). Следует внимательно относиться к тому, на какой стороне линии записывается пара: в нотации (min, max) по Элмасри и Навате пара ставится у сущности и говорит о её участии, тогда как в «вороньих лапках» символ у сущности говорит о том, сколько экземпляров этой сущности приходится на один экземпляр противоположной 3. Путаница сторон — самая частая ошибка чтения диаграмм.
Кардинальности — утверждения о предметной области, а не о текущих данных, но данные помогают проверить гипотезы. Например, утверждение «у книги до трёх авторов, а у автора может быть много книг» согласуется с учебной базой:
SELECT max(n) AS max_authors_per_book,
(SELECT max(cnt) FROM (SELECT count(*) AS cnt FROM bookstore.book_author
GROUP BY author_id) s) AS max_books_per_author
FROM (SELECT count(*) AS n FROM bookstore.book_author GROUP BY book_id) t;
max_authors_per_book | max_books_per_author
----------------------+----------------------
3 | 22
(1 row)
Частичное участие покупателя в связи «оформил» тоже видно по данным: 621 покупатель из 5000 не сделал ни одного заказа.
SELECT count(*) AS customers,
count(*) FILTER (WHERE NOT EXISTS (SELECT 1 FROM bookstore.orders o
WHERE o.customer_id = c.customer_id)) AS without_orders
FROM bookstore.customer c;
customers | without_orders
-----------+----------------
5000 | 621
(1 row)
Но если данные не опровергают правило, это ещё не доказывает его: правило «у книги не более трёх авторов» в учебной базе выполняется, хотя магазин наверняка продаёт сборники с десятью авторами. Кардинальности устанавливаются по требованиям, а данные лишь помогают найти противоречия; тот же принцип в теме 8 будет сформулирован для функциональных зависимостей.
Не все кардинальности одинаково легко обеспечить. Правило «у заказа ровно один покупатель» выражается столбцом orders.customer_id NOT NULL с внешним ключом, а правило «в заказе есть хотя бы одна позиция» — минимум 1 на стороне «многих» — внешним ключом не выражается: схема учебной базы позволяет создать пустой заказ.
BEGIN;
INSERT INTO bookstore.orders (customer_id, status, warehouse_id)
VALUES (1, 'new', 1)
RETURNING order_id, status;
ROLLBACK;
order_id | status
----------+--------
20001 | new
(1 row)
Номер заказа в выводе зависит от предыдущих экспериментов: значение генератора идентификаторов не возвращается при откате транзакции. Такие правила обеспечиваются триггерами, отложенными проверками (тема 2) или логикой приложения — оформлением заказа и позиций в одной транзакции (темы 12 и 14).
Слабые сущности, атрибуты связей и связи высокой степени
Слабые сущности и идентифицирующие связи
Сильная, или обычная, сущность (англ. strong entity) имеет собственный ключ. Слабая сущность (англ. weak entity) собственного ключа не имеет: её экземпляры различимы только в пределах экземпляра другой, владеющей (англ. owner, identifying) сущности 13. Связь слабой сущности с владельцем называется идентифицирующей (англ. identifying relationship); участие слабой сущности в ней всегда полное, а кардинальность со стороны владельца — 1.
Классический пример — позиция заказа с номером строки: «позиция 3» сама по себе ничего не обозначает, а «позиция 3 заказа № 5028» — определённая строка. Атрибут, различающий экземпляры слабой сущности в пределах владельца, называется частичным ключом (англ. partial key, discriminator), а полный ключ слабой сущности составляется из ключа владельца и частичного ключа. Другие примеры — том многотомного издания (том 2 книги «Война и мир»), квартира в доме, глава в книге.
Из определения следует поведение при удалении: экземпляр слабой сущности не может существовать без владельца, поэтому удаление владельца должно удалять и зависимые экземпляры (ON DELETE CASCADE, тема 2) или быть запрещено. В учебной базе позиции заказа (order_item) удаляются вместе с заказом, а первичный ключ (order_id, book_id) включает ключ заказа. Роль частичного ключа здесь играет не номер строки, а книга: в одном заказе одна книга встречается не более одного раза, а количество экземпляров хранится в атрибуте quantity. Поэтому order_item правильнее считать не слабой сущностью в чистом виде, а ассоциативной сущностью связи «заказ — книга» (следующий подраздел); граница между этими понятиями на практике размыта, и обе конструкции отображаются в таблицы одинаково.
Слабость — свойство предметной области, а не реализации: суррогатный ключ order_item_id не сделал бы позицию независимой от заказа.
Атрибуты связей и ассоциативные сущности
Атрибуты могут быть не только у сущностей, но и у связей 1. Порядок автора на обложке — не свойство книги (у книги несколько авторов) и не свойство автора (у автора несколько книг), а свойство пары «автор — книга», то есть атрибут связи «написал». Аналогично количество экземпляров книги на складе — атрибут связи «склад хранит книгу», а количество и цена в заказе — атрибуты связи «заказ содержит книгу».
Атрибут связи 1:N можно перенести на сторону «многих», а у связи M:N он остаётся атрибутом связи и попадает в таблицу связи: book_author.author_order, stock.quantity, order_item.quantity и order_item.price.
Если у связи появляются не только атрибуты, но и собственные связи с другими сущностями, её моделируют как ассоциативную сущность (англ. associative entity; в расширенной модели Элмасри и Наваты — агрегация) 39. Пусть магазин принимает возвраты: покупатель возвращает конкретную позицию заказа, и о возврате хранятся дата, причина и количество. Возврат связан не с заказом в целом и не с книгой вообще, а с парой «этот заказ — эта книга», то есть с экземпляром связи «содержит». Значит, «позиция заказа» должна быть полноправной сущностью, на которую может ссылаться «возврат». В реляционной схеме это означает внешний ключ (order_id, book_id) из таблицы возвратов на таблицу order_item — задача лабораторной работы 7.
Тернарные связи и когда их нельзя заменить бинарными
Связь степени 3 соединяет три сущности одновременно. Пусть магазин работает с поставщиками и нужно хранить, какой поставщик какую книгу на какой склад поставляет. Экземпляр связи «поставляет» — тройка (поставщик, книга, склад). Возникает соблазн заменить её тремя бинарными связями: «поставщик поставляет книгу», «книга поступает на склад», «поставщик работает со складом». Такая замена в общем случае теряет информацию 3.
Проверим это на маленьком примере. Пусть известно ровно три факта о поставках:
CREATE TABLE t07.supply (supplier text, book text, warehouse text,
PRIMARY KEY (supplier, book, warehouse));
INSERT INTO t07.supply VALUES
('Логос', 'Война и мир', 'Казань'),
('Логос', 'Основание', 'Москва'),
('Вектор', 'Война и мир', 'Москва');
Построим три бинарные проекции — таблицы sb (supplier, book), bw (book, warehouse), sw (supplier, warehouse) командами CREATE TABLE ... AS SELECT DISTINCT — и попробуем восстановить тройки, соединив их:
SELECT sb.supplier, sb.book, bw.warehouse
FROM t07.sb
JOIN t07.bw ON bw.book = sb.book
JOIN t07.sw ON sw.supplier = sb.supplier AND sw.warehouse = bw.warehouse
EXCEPT
SELECT * FROM t07.supply;
supplier | book | warehouse
----------+-------------+-----------
Логос | Война и мир | Москва
(1 row)
Соединение дало тройку, которой не было: «Логос» действительно поставляет «Войну и мир» (в Казань), «Война и мир» действительно поступает в Москву (от «Вектора»), «Логос» действительно работает с московским складом (поставляя «Основание»), но «Логос» не поставляет «Войну и мир» в Москву. Три бинарные связи описывают три проекции тернарной связи, а по проекциям исходное отношение в общем случае не восстанавливается. Это частный случай общего явления — декомпозиции с потерями, которое строго разбирается в теме 8 (там же — зависимости соединения и пятая нормальная форма, описывающие ровно тот случай, когда тернарную связь можно разложить на бинарные).
Отсюда правило моделирования: если факт по смыслу утверждает нечто об одновременном сочетании трёх сущностей («поставщик поставляет эту книгу на этот склад»), связь тернарная, и заменять её бинарными нельзя. Если же факты независимы («поставщик в принципе работает с этим складом» и «поставщик в принципе поставляет эту книгу»), в модели действительно две бинарные связи, а тернарная была бы ошибкой — она вынудила бы хранить все сочетания. Тернарную связь всегда можно корректно заменить ассоциативной сущностью «Поставка» с тремя обязательными бинарными связями «один ко многим» к поставщику, книге и складу; именно так тернарная связь и выглядит в нотациях, не допускающих связей степени выше двух (раздел о нотациях), и именно такой таблицей она реализуется.
Кардинальности тернарной связи читаются иначе, чем бинарной: число у сущности обозначает, сколько её экземпляров может приходиться на одну пару экземпляров двух других. Например, «1» у склада означает, что данный поставщик поставляет данную книгу не более чем на один склад; в реляционной схеме это превратится в ограничение UNIQUE (supplier, book).
Нотации ER-диаграмм
Нотация Чена
ER-модель обычно представляют диаграммой. Исходная нотация Чена 1 использует прямоугольники для сущностей, ромбы для связей и овалы для атрибутов; линии соединяют атрибуты с их владельцами и сущности со связями, а у линий связи пишутся кардинальности «1», «N», «M». Дополнительные обозначения, закрепившиеся в учебниках 32:
- ключевой атрибут подчёркивается;
- многозначный атрибут изображается двойным овалом, производный — пунктирным;
- составной атрибут — овалом, к которому присоединены овалы его частей;
- слабая сущность — двойным прямоугольником, идентифицирующая связь — двойным ромбом, частичный ключ подчёркивается пунктиром;
- полное участие — двойной линией, частичное — одинарной.
Диаграмма на рисунке ниже показывает фрагмент «Книжного узла» в нотации Чена: связь «написал» типа M:N с атрибутом «порядок», связь «выпустило» типа 1:N с частичным участием книги, составной атрибут «ФИО», многозначный атрибут «телефоны» и производный атрибут «число книг».
Достоинство нотации Чена — выразительность: связи — полноправные элементы диаграммы с именами и атрибутами, легко изображаются связи любой степени. Недостаток — громоздкость: диаграмма реальной схемы с сотней атрибутов-овалов нечитаема, поэтому в инженерной практике преобладает более компактная нотация «вороньих лапок».
Нотация «вороньи лапки»
В нотации «вороньи лапки» (англ. crow's foot notation) сущность изображается прямоугольником, внутри которого перечислены атрибуты, а связь — линией между прямоугольниками без ромба 52. Нотация получила распространение благодаря методологии информационного инжиниринга (англ. Information Engineering, IE) Дж. Мартина и К. Финкельштейна, поэтому её также называют нотацией IE; её вариант, описанный Р. Баркером, использовался в инструментах Oracle CASE 5.
Кардинальность и обязательность изображаются символами на концах линии, причём символ у сущности говорит о том, сколько её экземпляров может приходиться на один экземпляр сущности на другом конце. Внешний символ (ближе к прямоугольнику) задаёт максимум, внутренний — минимум:
- черта «|» — один, кружок «○» — ноль, «лапка» из трёх расходящихся линий — много;
- «||» — ровно один, «○|» — ноль или один, «|<» (лапка с чертой) — один или много, «○<» — ноль или много.
Связь читается от одной сущности к другой по символу у дальнего конца: «один покупатель оформляет ноль или много заказов» — смотрим на символ у заказа; «один заказ оформлен ровно одним покупателем» — на символ у покупателя. Связи M:N в логической модели «вороньих лапок» обычно сразу заменяют ассоциативной сущностью с двумя связями 1:N; на концептуальных диаграммах допускается и прямая линия с лапками на обоих концах. Многие инструменты различают сплошной линией идентифицирующую связь (ключ родителя входит в ключ потомка) и пунктирной — неидентифицирующую.
Диаграмма учебной базы «Книжный узел» в этой нотации приведена на рисунке ниже. Она построена по схеме schema.sql и запросам к системному каталогу из последнего раздела; для компактности показаны только ключевые и наиболее важные атрибуты.
По диаграмме читаются бизнес-правила из начала темы: у книги ноль или одно издательство («○|» у publisher), ровно одна рубрика, авторы через book_author; у рубрики ноль или один родитель; на заказ приходится ноль или несколько платежей. Видны и два расхождения схемы с требованиями. Со стороны позиций заказа стоит минимум «○», хотя заказ без позиций бессмыслен (схема этого не запрещает). Связь «заказ — склад» имеет вид «○|», так как warehouse_id допускает NULL, хотя правило «заказ отгружается с одного склада» подразумевает «||»; возможно, NULL разрешён сознательно — для нового заказа, склад которого ещё не выбран. Такие расхождения и есть предмет обсуждения при проектировании.
UML-диаграммы классов и IDEF1X
В объектно-ориентированной разработке для тех же целей используют диаграммы классов унифицированного языка моделирования (англ. Unified Modeling Language, UML), стандартизованного консорциумом OMG 6. Сущности соответствует класс с атрибутами, связи — ассоциация, а кардинальности записываются кратностями (англ. multiplicity) у концов ассоциации: 1, 0..1, * (то же, что 0..*), 1..*. Кратность, как и в «вороньих лапках», пишется у того класса, о числе экземпляров которого идёт речь. Атрибуты связи M:N изображаются классом-ассоциацией (англ. association class), присоединённым к линии ассоциации пунктиром; обобщение — линией с незаполненным треугольником у суперкласса; композиция (зависимая часть, аналог слабой сущности) — линией с закрашенным ромбом у владельца. Стандартного обозначения ключей в UML нет, его добавляют стереотипами или комментариями.
Нотация IDEF1X (англ. Integration Definition for Information Modeling) утверждена в США как федеральный стандарт FIPS PUB 184 в 1993 году 7. Она ближе всех к реляционной схеме: у каждой сущности над чертой перечисляются атрибуты первичного ключа, под чертой — остальные; внешние ключи помечаются (FK). Независимые сущности изображаются прямоугольниками с прямыми углами, зависимые (ключ которых включает ключ родителя) — со скруглёнными; идентифицирующая связь — сплошной линией, неидентифицирующая — пунктирной; на конце «многих» ставится закрашенная точка, возле которой может стоять P (один или более), Z (ноль или один) или число.
| Конструкция | Чен | «Вороньи лапки» | UML | IDEF1X |
|---|---|---|---|---|
| Сущность | прямоугольник | прямоугольник со списком атрибутов | класс | прямоугольник, ключ над чертой |
| Связь | ромб с именем | линия с подписью | ассоциация | линия с подписью |
| Кардинальность | 1, N, M у линии | символы на концах | кратности 0..1, 1..* | точка, P, Z |
| Обязательность | двойная линия | « | » или «○» внутри | нижняя граница кратности |
| Атрибут связи | овал у ромба | ассоциативная сущность | класс-ассоциация | ассоциативная сущность |
| Связь степени 3 и выше | ромб с тремя линиями | только через ассоциативную сущность | ромб n-арной ассоциации | только через ассоциативную сущность |
| Слабая сущность | двойной прямоугольник | сплошная линия идентифицирующей связи | композиция | скруглённый прямоугольник |
| Обобщение | треугольник или круг «ISA» | зависит от инструмента | незаполненный треугольник | круг категоризации |
В курсе нотация Чена используется для объяснения понятий, «вороньи лапки» — для диаграмм схем.
Обобщение, специализация и наследование в таблицах
Подтипы и супертипы
Часто экземпляры одной сущности делятся на группы с разными наборами атрибутов. Покупателем магазина может быть физическое лицо (ФИО, дата рождения) или организация (наименование, ИНН, КПП); у обоих есть адрес электронной почты, история заказов и отзывы. Расширенная модель «сущность — связь» (англ. Enhanced ER, EER) описывает такую ситуацию отношением «супертип — подтип» (англ. supertype, subtype), или «суперкласс — подкласс» 39.
Специализация (англ. specialization) — процесс выделения подтипов в существующей сущности: «покупатель» специализируется на «физическое лицо» и «организацию». Обобщение (англ. generalization) — обратный процесс: из сущностей «физическое лицо» и «организация» с общими атрибутами выделяется общий супертип «покупатель». Понятие обобщения как абстракции, на равных с агрегацией, ввели в теорию баз данных Дж. и Д. Смит в 1977 году 9. Экземпляр подтипа является и экземпляром супертипа (связь «является», англ. is-a) и наследует все его атрибуты и связи; у подтипа могут быть собственные атрибуты и собственные связи: например, только с организацией связан «договор поставки».
Специализацию характеризуют два независимых ограничения:
- непересечение (англ. disjointness): специализация непересекающаяся (англ. disjoint), если экземпляр может принадлежать не более чем одному подтипу (покупатель — либо физическое лицо, либо организация), и перекрывающаяся (англ. overlapping), если нескольким (человек может быть одновременно автором и покупателем);
- полнота (англ. completeness): специализация полная (англ. total), если каждый экземпляр супертипа принадлежит хотя бы одному подтипу, и частичная (англ. partial), если могут существовать экземпляры супертипа без подтипа.
Специализация «физическое лицо / организация» — непересекающаяся и полная. Подтипы оправданы, только если у них разные атрибуты или связи; иначе достаточно атрибута «категория».
Три способа отображения иерархии в таблицы
Реляционная модель не знает наследования, поэтому иерархию типов приходится отображать в таблицы. Существуют три основных способа; в литературе по объектно-реляционному отображению их называют по Фаулеру 8, в учебниках по базам данных — по стилю: «стиль ER», «объектно-ориентированный стиль», «стиль с NULL» 10. Схема на рисунке ниже сравнивает их для покупателей — физических лиц и организаций.
Одна таблица на иерархию (англ. single table inheritance): все атрибуты всех подтипов собираются в одну таблицу, добавляется столбец-признак типа (англ. discriminator). Атрибуты чужого подтипа остаются пустыми, а правило «у физического лица есть ФИО и нет ИНН» выражается ограничением CHECK:
CREATE TABLE t07.client_single (
client_id integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
email text NOT NULL UNIQUE,
kind text NOT NULL CHECK (kind IN ('person', 'org')),
full_name text, -- только для физического лица
org_name text, -- только для организации
inn text UNIQUE, -- только для организации
CHECK (kind = 'person' AND full_name IS NOT NULL AND org_name IS NULL AND inn IS NULL
OR kind = 'org' AND org_name IS NOT NULL AND inn IS NOT NULL AND full_name IS NULL)
);
INSERT INTO t07.client_single (email, kind, full_name, inn)
VALUES ('x@example.com', 'person', 'Иван Жуков', '7700000000');
ERROR: new row for relation "client_single" violates check constraint "client_single_check"
DETAIL: Failing row contains (3, x@example.com, person, Иван Жуков, null, 7700000000).
Таблица на каждый класс (англ. class table inheritance): общие атрибуты хранятся в таблице супертипа, собственные атрибуты каждого подтипа — в отдельной таблице, первичный ключ которой одновременно является внешним ключом на супертип (связь 1:1). Чтобы СУБД обеспечивала и непересечение — одна строка client не может иметь и «физическое», и «юридическое» продолжение, — признак типа включают во внешний ключ:
CREATE TABLE t07.client (
client_id integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
email text NOT NULL UNIQUE,
kind text NOT NULL CHECK (kind IN ('person', 'org')),
UNIQUE (client_id, kind)
);
CREATE TABLE t07.client_person (
client_id integer PRIMARY KEY,
kind text NOT NULL DEFAULT 'person' CHECK (kind = 'person'),
full_name text NOT NULL,
FOREIGN KEY (client_id, kind) REFERENCES t07.client (client_id, kind) ON DELETE CASCADE
);
-- client_org — аналогично: kind = 'org', org_name, inn NOT NULL UNIQUE
После вставки физического лица с client_id = 1 попытка добавить ему «организационное» продолжение отвергается:
INSERT INTO t07.client_org (client_id, org_name, inn) VALUES (1, 'Лицей № 1', '7701234567');
ERROR: insert or update on table "client_org" violates foreign key constraint "client_org_client_id_kind_fkey"
DETAIL: Key (client_id, kind)=(1, org) is not present in table "client".
Полноту (у каждой строки client есть продолжение) декларативно обеспечить не удаётся — нужна транзакция, вставляющая обе строки, или триггер.
Таблица на конкретный класс (англ. concrete table inheritance): таблицы создаются только для подтипов, и каждая содержит все атрибуты — и унаследованные, и собственные. Таблицы супертипа нет. Цена — потеря общих ограничений: уникальность адреса электронной почты и идентификатора теперь обеспечивается в каждой таблице отдельно, и ничто не мешает физическому лицу и организации получить одинаковые значения:
SELECT client_id, email, 'person' AS kind FROM t07.person_client
UNION ALL
SELECT client_id, email, 'org' FROM t07.org_client;
client_id | email | kind
-----------+------------------+--------
1 | same@example.com | person
1 | same@example.com | org
(2 rows)
Кроме того, на «покупателя вообще» нельзя сослаться внешним ключом: таблице заказов понадобятся два необязательных внешних ключа с проверкой «заполнен ровно один».
Сравнение способов
| Критерий | Одна таблица | Таблица на класс | Таблица на конкретный класс |
|---|---|---|---|
| Число таблиц | 1 | 1 + число подтипов | число подтипов |
| Пустые значения | много (атрибуты чужих подтипов) | нет | нет |
| Обязательность атрибутов подтипа | только через CHECK | NOT NULL в таблице подтипа | NOT NULL |
| Внешний ключ на супертип | да | да | нет |
| Запрос «все покупатели» | одна таблица | одна таблица супертипа | UNION ALL |
| Запрос «все сведения об организации» | одна таблица | соединение двух таблиц | одна таблица |
| Непересечение | автоматически (один признак) | приёмом с (id, kind) | не обеспечивается |
| Перекрывающаяся специализация | неудобно | естественно | дублирование данных |
| Добавление подтипа | ALTER TABLE и новый CHECK | новая таблица | новая таблица и правка всех UNION |
Одна таблица проста и быстра, когда подтипов мало и собственных атрибутов немного; таблица на класс точнее всего отражает модель, но требует соединений; таблица на конкретный класс уместна, если на супертип никто не ссылается. В PostgreSQL есть собственный механизм наследования таблиц (CREATE TABLE ... INHERITS), но ограничения уникальности и внешние ключи на нём не распространяются на всю иерархию, что документация прямо оговаривает как ограничение механизма 13; поэтому для моделирования подтипов его обычно не применяют, а используют для других задач (исторически — для секционирования, тема 9).
Преобразование ER-модели в реляционную схему
Правила для сущностей и атрибутов
Преобразование ER-модели в реляционную схему выполняется по набору правил, которые почти не требуют творческих решений 311. Для сущностей и атрибутов они таковы.
- Сильная сущность становится таблицей. Простые однозначные атрибуты — столбцами; один из потенциальных ключей выбирается первичным ключом, остальные объявляются
UNIQUE; при необходимости вводится суррогатный ключ (тема 2). Необязательные атрибуты допускаютNULL, обязательные получаютNOT NULL. - Составной атрибут раскладывается на простые столбцы-части (
last_name,first_name); сам составной атрибут как целое в таблице не хранится. - Многозначный атрибут выносится в отдельную таблицу с внешним ключом на владельца и первичным ключом «ключ владельца + значение»: телефоны издательства — таблица
publisher_phone (publisher_id, phone). Хранить несколько значений в одном столбце через запятую — нарушение первой нормальной формы, последствия которого разобраны в теме 8. - Производный атрибут обычно не хранится, а вычисляется запросом или представлением; если вычисление дорого, его можно хранить, но тогда нужен механизм поддержания согласованности — это сознательная денормализация (тема 8, механизмы — тема 14).
- Слабая сущность становится таблицей, в которую добавляется внешний ключ на владельца; первичный ключ — ключ владельца плюс частичный ключ; внешний ключ обязательный, обычно с
ON DELETE CASCADE.
Правила для связей
Связи отображаются по-разному в зависимости от кардинальности.
- Связь 1:N реализуется внешним ключом в таблице стороны «многих», ссылающимся на таблицу стороны «одного»:
book.publisher_idссылается наpublisher. Полное участие стороны «многих» превращается вNOT NULL(book.category_id), частичное — в допустимостьNULL(book.publisher_id). Атрибуты связи переносятся в ту же таблицу. - Связь 1:1 реализуется внешним ключом в одной из таблиц с ограничением
UNIQUEна этом столбце — именноUNIQUEотличает 1:1 от 1:N. Внешний ключ ставят на стороне с полным участием: если каждая карта лояльности принадлежит покупателю, а не каждый покупатель имеет карту, столбецcustomer_id NOT NULL UNIQUEразмещают в таблице карт. Если участие обеих сторон полное и у сущностей нет собственных связей, их можно слить в одну таблицу. - Связь M:N всегда требует отдельной таблицы связи (англ. junction table, associative table) с внешними ключами на обе сущности; первичный ключ — пара внешних ключей (если одна и та же пара не может встречаться дважды), атрибуты связи — дополнительные столбцы:
book_author (book_id, author_id, author_order). - Связь степени n реализуется таблицей с n внешними ключами. Её первичный ключ определяется кардинальностями: если все стороны «многие», ключ — все n внешних ключей; если у одной стороны «1», эта сторона в ключ не входит, как в примере с поставками:
UNIQUE (supplier, book). - Рекурсивная связь реализуется внешним ключом таблицы на саму себя (1:N, как
category.parent_id) или таблицей связи с двумя внешними ключами на одну таблицу (M:N, например «книга рекомендуется вместе с книгой»); имена столбцов отражают роли. - Иерархия подтипов — одним из трёх способов, рассмотренных выше.
Правила затрагивают только структуру. Бизнес-правила, не выражаемые кардинальностями, превращаются в ограничения CHECK (оценка от 1 до 5), UNIQUE (один отзыв на книгу от покупателя), исключающие ограничения или триггеры (тема 14). Схема, полученная по этим правилам из аккуратной ER-модели, обычно находится в третьей нормальной форме, но гарантии этого нет: если в сущность попал атрибут, зависящий от другого неключевого атрибута (город издательства в сущности «Книга»), правила перенесут избыточность в таблицу. Строгая проверка — предмет темы 8.
Восстановление модели по системному каталогу
Часто решается обратная задача — понять модель существующей базы без документации; её называют обратным проектированием (англ. reverse engineering). Нужная информация есть в системном каталоге: таблицы, столбцы, допустимость NULL, первичные, уникальные и внешние ключи. Стандарт SQL определяет для доступа к ней информационную схему information_schema — набор представлений, одинаковый (с точностью до особенностей реализации) в разных СУБД 12.
Связи между таблицами — это внешние ключи. Их перечисляет следующий запрос: представление table_constraints содержит ограничения, key_column_usage — столбцы, на которых ограничение определено, constraint_column_usage — столбцы, на которые оно ссылается, columns — допустимость NULL:
SELECT tc.table_name AS child,
kcu.column_name AS fk_column,
ccu.table_name AS parent,
c.is_nullable AS nullable
FROM information_schema.table_constraints tc
JOIN information_schema.key_column_usage kcu
ON kcu.constraint_schema = tc.constraint_schema
AND kcu.constraint_name = tc.constraint_name
JOIN information_schema.constraint_column_usage ccu
ON ccu.constraint_schema = tc.constraint_schema
AND ccu.constraint_name = tc.constraint_name
JOIN information_schema.columns c
ON c.table_schema = tc.table_schema
AND c.table_name = tc.table_name
AND c.column_name = kcu.column_name
WHERE tc.constraint_type = 'FOREIGN KEY'
AND tc.table_schema = 'bookstore'
ORDER BY child, fk_column;
child | fk_column | parent | nullable
-------------+--------------+-----------+----------
book | category_id | category | NO
book | publisher_id | publisher | YES
book_author | author_id | author | NO
book_author | book_id | book | NO
category | parent_id | category | YES
order_item | book_id | book | NO
order_item | order_id | orders | NO
orders | customer_id | customer | NO
orders | warehouse_id | warehouse | YES
payment | order_id | orders | NO
review | book_id | book | NO
review | customer_id | customer | NO
stock | book_id | book | NO
stock | warehouse_id | warehouse | NO
(14 rows)
Каждая строка — связь «один ко многим» от parent к child; столбец nullable даёт минимум на стороне родителя: NO — «ровно один» (||), YES — «ноль или один» (○|). Таблицы, у которых два внешних ключа входят в первичный ключ (book_author, stock, order_item), — ассоциативные сущности, реализующие связи M:N; столбцы первичных ключей перечисляет то же представление key_column_usage при constraint_type = 'PRIMARY KEY'. Строка category → category — рекурсивная связь. Связей 1:1 в учебной базе нет: их выдало бы ограничение UNIQUE на столбце внешнего ключа. Чего каталог не расскажет — это минимум на стороне «многих» (есть ли у каждой книги хотя бы один автор) и смысл связей: их имена восстанавливаются по названиям таблиц и знанию предметной области.
Запрос имеет два ограничения, которые нужно знать. Во-первых, constraint_column_usage не сообщает, какой столбец родителя соответствует какому столбцу потомка, поэтому для составных внешних ключей запрос даст декартово произведение столбцов; для них нужно соединять key_column_usage с referential_constraints по position_in_unique_constraint или обращаться к каталогу PostgreSQL pg_constraint (массивы conkey и confkey). Во-вторых, представления информационной схемы показывают только объекты, к которым у текущей роли есть доступ, а constraint_column_usage — только таблицы, принадлежащие текущей роли 12. Действия при удалении (ON DELETE CASCADE у book_author.book_id и order_item.order_id) можно получить из представления referential_constraints: столбец delete_rule.
По результату такого запроса можно автоматически построить текст диаграммы для инструментов, принимающих текстовое описание. Например, в синтаксисе Mermaid 14 связи учебной базы записываются строками вида:
erDiagram
publisher |o--o{ book : "выпускает"
category ||--o{ book : "содержит"
category |o--o{ category : "родитель"
book ||--o{ book_author : ""
author ||--o{ book_author : ""
Здесь || и |o — «ровно один» и «ноль или один», o{ и |{ — «ноль или много» и «один или много»; два дефиса обозначают идентифицирующую связь (сплошная линия), две точки — неидентифицирующую (пунктир). Построение полной диаграммы учебной базы по каталогу — задание лабораторной работы 7.
Кроме текстовых описаний диаграмм (Mermaid, PlantUML, язык DBML сервиса dbdiagram.io) существуют графические редакторы (draw.io), средства моделирования с генерацией DDL и обратным проектированием (pgModeler, ER-редактор DBeaver) и генераторы документации по живой базе (SchemaSpy). Текстовые описания удобно хранить в системе контроля версий рядом с миграциями схемы (тема 15).
Типичные ошибки моделирования
Атрибут вместо сущности. Если понятие имеет собственные свойства или участвует в связях, его нельзя делать атрибутом. Город покупателя и город склада в учебной базе — текстовые атрибуты; это оправдано, пока о городе ничего не нужно хранить. Если появится требование «срок доставки зависит от пары „город склада — город покупателя“», город станет сущностью, иначе срок пришлось бы привязывать к строке текста, в которой «Санкт-Петербург» и «С.-Петербург» — разные значения. Самый распространённый вариант этой ошибки — список через запятую: «авторы» как текстовое поле книги вместо связи M:N; его последствия подробно разбираются в теме 8 и в лабораторной работе 8.
Сущность вместо атрибута — обратная ошибка: сущность «Переплёт» с единственным атрибутом «название». Она безвредна для целостности, но добавляет соединения; выбор между справочником, перечислимым типом и CHECK обсуждается в теме 9.
Связь вместо сущности. Связь, у которой появились собственные связи, должна стать ассоциативной сущностью (пример с возвратами выше). Признак — фраза вида «… относится к конкретной позиции заказа».
Ловушка «веер» (англ. fan trap) возникает, когда из одной сущности выходят две связи 1:N, а вопрос касается связи между двумя «дальними» сущностями 4. Пусть модель содержит «Город — Склад» (1:N) и «Город — Покупатель» (1:N). Вопрос «с какого склада обслуживается покупатель» по такой модели не решается: если в городе два склада, путь «покупатель — город — склад» даёт оба. Связь, которую хотели выразить, либо прямая («покупатель прикреплён к складу»), либо определяется событиями («заказ отгружен со склада»), и её нужно моделировать явно. В учебной базе «веер» не возникает потому, что склад указан непосредственно в заказе (orders.warehouse_id), а не выводится через город.
Ловушка «разрыв» (англ. chasm trap) возникает, когда путь между сущностями существует, но проходит через связь с частичным участием, и для части экземпляров обрывается 4. Если город издания книги получать через издательство, книги без издательства (или издательства без города) из ответа выпадают:
SELECT count(*) AS books, count(p.city) AS with_pub_city
FROM bookstore.book b
LEFT JOIN bookstore.publisher p USING (publisher_id)
WHERE b.category_id = 12; -- рубрика «Программирование»
books | with_pub_city
-------+---------------
119 | 99
Для двадцати книг рубрики, включая «Искусство программирования» Кнута без издательства, город издания так не узнать; если он важен, это должен быть атрибут книги, а не вывод через необязательную связь.
Избыточная связь выводится из других: при наличии «Покупатель оформил заказ» и «Заказ отгружен со склада» связь «Покупатель получал книги со склада» — композиция двух первых, и её отдельное хранение грозит рассогласованием. Но не всякая связь, похожая на композицию, избыточна: «покупатель оставил отзыв о книге» не выводится из «покупатель заказал книгу» — купленную книгу можно не оценивать, а правила магазина могут разрешать отзывы на книги, купленные в другом месте. Избыточность определяется по смыслу, а не по форме диаграммы.
Итоги темы
Проектирование базы данных разделяется на этапы, отвечающие на вопросы разной природы: анализ требований фиксирует словарь и бизнес-правила, концептуальное проектирование описывает предметную область в терминах сущностей и связей, логическое — переводит описание в реляционную схему, физическое — в объекты конкретной СУБД. Концептуальная модель не зависит от реализации, служит языком общения с заказчиком и позволяет обнаружить ошибки до того, как в схеме появятся данные.
Модель «сущность — связь» описывает предметную область сущностями с ключами, атрибутами разных видов и связями, у которых есть степень, роли, кардинальность и обязательность участия. Слабые сущности идентифицируются через владельца, связи могут иметь атрибуты и становиться ассоциативными сущностями, а тернарная связь в общем случае не сводится к трём бинарным: её проекции, соединённые обратно, порождают ложные факты.
Нотации различаются выразительностью и компактностью: нотация Чена удобна для объяснения понятий, «вороньи лапки» и IDEF1X — для схем реального размера, UML — когда модель данных обсуждается вместе с программой. Иерархии подтипов отображаются в таблицы тремя способами — одной таблицей, таблицей на класс или таблицей на конкретный класс, — и выбор между ними — компромисс между числом пустых значений, обеспечением ограничений средствами СУБД и стоимостью запросов.
Преобразование ER-модели в таблицы выполняется по почти механическим правилам: сущность — таблица, связь 1:N — внешний ключ, 1:1 — внешний ключ с UNIQUE, M:N и n-арная связь — отдельная таблица. Обратный путь — восстановление модели по системному каталогу — показан на учебной базе запросом к information_schema. Типичные ошибки — атрибут вместо сущности, ловушки «веер» и «разрыв», избыточные связи — обнаруживаются по смыслу модели, а не по её форме.
Правила преобразования гарантируют корректную структуру, но не отсутствие избыточности: если в сущность попал атрибут, зависящий не от ключа, а от другого атрибута, таблица унаследует аномалии обновления. Строгий аппарат для обнаружения и устранения таких проблем — функциональные зависимости и нормальные формы — рассматривается в теме 8.
Литература
- Chen P. P.-S. The Entity-Relationship Model — Toward a Unified View of Data. — ACM Transactions on Database Systems, 1976, Т. 1, № 1, С. 9–36, DOI: 10.1145/320434.320440.
- Silberschatz A., Korth H. F., Sudarshan S. Database System Concepts. 7th ed. — McGraw-Hill, 2019.
- Elmasri R., Navathe S. B. Fundamentals of Database Systems. 7th ed. — Pearson, 2016.
- Connolly T., Begg C. Database Systems: A Practical Approach to Design, Implementation, and Management. 6th ed. — Pearson, 2015.
- Barker R. CASE*Method: Entity Relationship Modelling. — Addison-Wesley, 1990.
- Object Management Group. OMG Unified Modeling Language (OMG UML). Version 2.5.1. — OMG, 2017. https://www.omg.org/spec/UML/2.5.1
- National Institute of Standards and Technology. Integration Definition for Information Modeling (IDEF1X). — FIPS PUB 184, 1993.
- Fowler M. Patterns of Enterprise Application Architecture. — Addison-Wesley, 2002.
- Smith J. M., Smith D. C. P. Database Abstractions: Aggregation and Generalization. — ACM Transactions on Database Systems, 1977, Т. 2, № 2, С. 105–133, DOI: 10.1145/320544.320546.
- Garcia-Molina H., Ullman J. D., Widom J. Database Systems: The Complete Book. 2nd ed. — Pearson Prentice Hall, 2008.
- Teorey T. J., Yang D., Fry J. P. A Logical Design Methodology for Relational Databases Using the Extended Entity-Relationship Model. — ACM Computing Surveys, 1986, Т. 18, № 2, С. 197–222, DOI: 10.1145/7474.7475.
- PostgreSQL Global Development Group. PostgreSQL 16 Documentation. The Information Schema. — 2024. https://www.postgresql.org/docs/16/information-schema.html
- PostgreSQL Global Development Group. PostgreSQL 16 Documentation. Section 5.10. Inheritance. — 2024. https://www.postgresql.org/docs/16/ddl-inherit.html
- Mermaid. Entity Relationship Diagrams. — Документация проекта Mermaid. https://mermaid.js.org/syntax/entityRelationshipDiagram.html