Тема 07

Концептуальное проектирование: модель «сущность — связь»

Концептуальное проектирование: модель «сущность — связь»

В темах 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 и запросам к системному каталогу из последнего раздела; для компактности показаны только ключевые и наиболее важные атрибуты.

ER-диаграмма учебной базы «Книжный узел»
Двенадцать таблиц учебной базы в нотации «вороньи лапки»: PK — первичный ключ, FK — внешний ключ, UK — уникальный; связи M:N реализованы ассоциативными сущностями book_author, stock и order_item

По диаграмме читаются бизнес-правила из начала темы: у книги ноль или одно издательство («○|» у 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 (ноль или один) или число.

КонструкцияЧен«Вороньи лапки»UMLIDEF1X
Сущностьпрямоугольникпрямоугольник со списком атрибутовкласспрямоугольник, ключ над чертой
Связьромб с именемлиния с подписьюассоциациялиния с подписью
Кардинальность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. Схема на рисунке ниже сравнивает их для покупателей — физических лиц и организаций.

Три способа отображения иерархии «покупатель — физическое лицо / организация»
Одна таблица на иерархию с признаком типа; таблица на каждый класс со связью 1:1 по первичному ключу; таблица только на конкретные классы с дублированием общих атрибутов

Одна таблица на иерархию (англ. 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)

Кроме того, на «покупателя вообще» нельзя сослаться внешним ключом: таблице заказов понадобятся два необязательных внешних ключа с проверкой «заполнен ровно один».

Сравнение способов

КритерийОдна таблицаТаблица на классТаблица на конкретный класс
Число таблиц11 + число подтиповчисло подтипов
Пустые значениямного (атрибуты чужих подтипов)нетнет
Обязательность атрибутов подтипатолько через CHECKNOT NULL в таблице подтипаNOT NULL
Внешний ключ на супертипдаданет
Запрос «все покупатели»одна таблицаодна таблица супертипаUNION ALL
Запрос «все сведения об организации»одна таблицасоединение двух таблицодна таблица
Непересечениеавтоматически (один признак)приёмом с (id, kind)не обеспечивается
Перекрывающаяся специализациянеудобноестественнодублирование данных
Добавление подтипаALTER TABLE и новый CHECKновая таблицановая таблица и правка всех UNION

Одна таблица проста и быстра, когда подтипов мало и собственных атрибутов немного; таблица на класс точнее всего отражает модель, но требует соединений; таблица на конкретный класс уместна, если на супертип никто не ссылается. В PostgreSQL есть собственный механизм наследования таблиц (CREATE TABLE ... INHERITS), но ограничения уникальности и внешние ключи на нём не распространяются на всю иерархию, что документация прямо оговаривает как ограничение механизма 13; поэтому для моделирования подтипов его обычно не применяют, а используют для других задач (исторически — для секционирования, тема 9).

Преобразование ER-модели в реляционную схему

Правила для сущностей и атрибутов

Преобразование ER-модели в реляционную схему выполняется по набору правил, которые почти не требуют творческих решений 311. Для сущностей и атрибутов они таковы.

  1. Сильная сущность становится таблицей. Простые однозначные атрибуты — столбцами; один из потенциальных ключей выбирается первичным ключом, остальные объявляются UNIQUE; при необходимости вводится суррогатный ключ (тема 2). Необязательные атрибуты допускают NULL, обязательные получают NOT NULL.
  2. Составной атрибут раскладывается на простые столбцы-части (last_name, first_name); сам составной атрибут как целое в таблице не хранится.
  3. Многозначный атрибут выносится в отдельную таблицу с внешним ключом на владельца и первичным ключом «ключ владельца + значение»: телефоны издательства — таблица publisher_phone (publisher_id, phone). Хранить несколько значений в одном столбце через запятую — нарушение первой нормальной формы, последствия которого разобраны в теме 8.
  4. Производный атрибут обычно не хранится, а вычисляется запросом или представлением; если вычисление дорого, его можно хранить, но тогда нужен механизм поддержания согласованности — это сознательная денормализация (тема 8, механизмы — тема 14).
  5. Слабая сущность становится таблицей, в которую добавляется внешний ключ на владельца; первичный ключ — ключ владельца плюс частичный ключ; внешний ключ обязательный, обычно с ON DELETE CASCADE.

Правила для связей

Связи отображаются по-разному в зависимости от кардинальности.

  1. Связь 1:N реализуется внешним ключом в таблице стороны «многих», ссылающимся на таблицу стороны «одного»: book.publisher_id ссылается на publisher. Полное участие стороны «многих» превращается в NOT NULL (book.category_id), частичное — в допустимость NULL (book.publisher_id). Атрибуты связи переносятся в ту же таблицу.
  2. Связь 1:1 реализуется внешним ключом в одной из таблиц с ограничением UNIQUE на этом столбце — именно UNIQUE отличает 1:1 от 1:N. Внешний ключ ставят на стороне с полным участием: если каждая карта лояльности принадлежит покупателю, а не каждый покупатель имеет карту, столбец customer_id NOT NULL UNIQUE размещают в таблице карт. Если участие обеих сторон полное и у сущностей нет собственных связей, их можно слить в одну таблицу.
  3. Связь M:N всегда требует отдельной таблицы связи (англ. junction table, associative table) с внешними ключами на обе сущности; первичный ключ — пара внешних ключей (если одна и та же пара не может встречаться дважды), атрибуты связи — дополнительные столбцы: book_author (book_id, author_id, author_order).
  4. Связь степени n реализуется таблицей с n внешними ключами. Её первичный ключ определяется кардинальностями: если все стороны «многие», ключ — все n внешних ключей; если у одной стороны «1», эта сторона в ключ не входит, как в примере с поставками: UNIQUE (supplier, book).
  5. Рекурсивная связь реализуется внешним ключом таблицы на саму себя (1:N, как category.parent_id) или таблицей связи с двумя внешними ключами на одну таблицу (M:N, например «книга рекомендуется вместе с книгой»); имена столбцов отражают роли.
  6. Иерархия подтипов — одним из трёх способов, рассмотренных выше.

Правила затрагивают только структуру. Бизнес-правила, не выражаемые кардинальностями, превращаются в ограничения 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.

Литература

  1. 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.
  2. Silberschatz A., Korth H. F., Sudarshan S. Database System Concepts. 7th ed. — McGraw-Hill, 2019.
  3. Elmasri R., Navathe S. B. Fundamentals of Database Systems. 7th ed. — Pearson, 2016.
  4. Connolly T., Begg C. Database Systems: A Practical Approach to Design, Implementation, and Management. 6th ed. — Pearson, 2015.
  5. Barker R. CASE*Method: Entity Relationship Modelling. — Addison-Wesley, 1990.
  6. Object Management Group. OMG Unified Modeling Language (OMG UML). Version 2.5.1. — OMG, 2017. https://www.omg.org/spec/UML/2.5.1
  7. National Institute of Standards and Technology. Integration Definition for Information Modeling (IDEF1X). — FIPS PUB 184, 1993.
  8. Fowler M. Patterns of Enterprise Application Architecture. — Addison-Wesley, 2002.
  9. 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.
  10. Garcia-Molina H., Ullman J. D., Widom J. Database Systems: The Complete Book. 2nd ed. — Pearson Prentice Hall, 2008.
  11. 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.
  12. PostgreSQL Global Development Group. PostgreSQL 16 Documentation. The Information Schema. — 2024. https://www.postgresql.org/docs/16/information-schema.html
  13. PostgreSQL Global Development Group. PostgreSQL 16 Documentation. Section 5.10. Inheritance. — 2024. https://www.postgresql.org/docs/16/ddl-inherit.html
  14. Mermaid. Entity Relationship Diagrams. — Документация проекта Mermaid. https://mermaid.js.org/syntax/entityRelationshipDiagram.html