Ваша таблица Revisions
, как вы показали, моделирует отношение «многие ко многим» между Posts
и Text
. Это, вероятно, , а не , что вы хотите, если только данная строка в Text
не может предоставить содержимое для нескольких строк в Posts
. Это не то, как работает большинство архитектур CMS.
Тебе точно не нужны три стола. Я понятия не имею, почему вы думаете, что это необходимо для 3NF. Суть 3NF заключается в том, что атрибут не должен зависеть от неключевого атрибута, он не говорит о том, что вы должны разбиваться на несколько таблиц без необходимости.
Таким образом, вам может понадобиться только отношение «один ко многим» между двумя таблицами: Posts
и Revisions
. То есть для каждого поста может быть несколько ревизий, но данная ревизия применяется только к одному посту. Другие предложили две альтернативы для поиска текущей записи:
Флаговый столбец в Revisions
, чтобы отметить текущую версию. Изменить текущую ревизию так же просто, как поменять флаг на true в желаемой ревизии и на false для ранее текущей ревизии.
Внешний ключ в Posts
для ревизии, которая является актуальной для данного сообщения. Это еще проще, потому что вы можете изменить текущую редакцию в одном обновлении вместо двух. Но циклические ссылки на внешние ключи могут вызвать проблемы с резервным копированием и восстановлением, каскадными обновлениями и т. Д.
Вы даже можете внедрить систему ревизий, используя одну таблицу :
CREATE TABLE PostRevisions (
post_revision_id SERIAL PRIMARY KEY,
post_id INT NOT NULL,
is_current TINYINT NULL,
date DATE,
title VARCHAR(80) NOT NULL,
text TEXT NOT NULL,
UNIQUE KEY (post_id, is_current)
);
Я не уверен, что дублирование для хранения title
с каждой ревизией дублируется, потому что название может быть пересмотрено так же, как и текст, не так ли?
Столбец is_current
должен быть либо 1, либо NULL. Уникальное ограничение не считает NULL, поэтому вы можете иметь только одну строку, где is_current
равно 1, и неограниченное количество строк, где оно равно NULL.
Это требует обновления двух строк, чтобы сделать ревизию текущей, но вы получаете некоторую простоту, сводя модель к одной таблице. Это большое преимущество при использовании ORM.
Вы можете создать представление, чтобы упростить общий случай запроса текущих сообщений:
CREATE VIEW Posts AS SELECT * FROM PostRevisions WHERE is_current = 1;
обновление: Относительно вашего обновленного вопроса: я согласен, что правильный реляционный дизайн будет поощрять две таблицы, чтобы вы могли сделать несколько атрибутов Post
инварианта для всех ревизий этого поста. Но большинство инструментов ORM предполагают, что сущность существует в одной таблице, а ORM неуклюжи при соединении строк из нескольких таблиц, чтобы образовать данную сущность. Поэтому я бы сказал, что если использование ORM является приоритетом, вы должны хранить сообщения и ревизии в одной таблице. Пожертвуйте немного правильности отношений, чтобы поддержать предположения парадигмы ORM.
Еще одно предложение - рассмотреть Измерение размеров . Это школа проектирования баз данных для поддержки OLAP и хранилищ данных. Он разумно использует денормализацию, поэтому вы обычно можете организовать данные в Star Schema . Основная сущность («Таблица фактов») представлена одной таблицей, поэтому это было бы выигрышно для ORM-ориентированного дизайна приложения.