Анализируя сценарий, который представляет характеристики, связанные с предметом, известным как временные базы данных, с концептуальной точки зрения можно определить, что: (а) «настоящая» версия блоговой статьи и (б) «прошлая» версия блог-сюжета , хотя и очень напоминающие, являются сущностями разных типов.
Кроме того, при работе на логическом уровне абстракции факты (представленные строками) разных видов должны храниться в разных таблицах. В рассматриваемом случае, даже если они очень похожи, (i) факты о «настоящих» версиях отличаются от (ii) фактов о «прошлых» версиях .
Поэтому я рекомендую управлять ситуацией с помощью двух таблиц:
один предназначено исключительно для «текущей» или «настоящей» Версии этих историй Дневника , и
один, который является отдельным, но также связан с другим, для всех «предыдущих» или «прошлых» версий ;
каждый с (1) немного отличным количеством столбцов и (2) другой группой ограничений.
Возвращаясь к концептуальному уровню, я считаю, что - в вашей бизнес-среде - автор и редактор - это понятия, которые можно разграничить как роли, которые могут быть воспроизведены пользователем , и эти важные аспекты зависят от вывода данных (посредством операций манипуляции на логическом уровне) и интерпретация (осуществляется читателями и авторами блогов на внешнем уровне компьютеризированной информационной системы с помощью одной или нескольких прикладных программ).
Я подробно опишу все эти факторы и другие важные моменты следующим образом.
Бизнес правила
В соответствии с моим пониманием ваших требований, следующие формулировки бизнес-правил (собранные в терминах соответствующих типов сущностей и их видов взаимосвязей) особенно полезны при создании соответствующей концептуальной схемы:
- Пользователь пишет ноль-один-или-многих BlogStories
- BlogStory имеет нулевой один или многим BlogStoryVersions
- Пользователь написал ноль-один-или-многих BlogStoryVersions
Описательная схема IDEF1X
Следовательно, для того , чтобы изложить свое предложение в силе графического устройства, я создал образец IDEF1x диаграмма , которая является производной от бизнеса - правил , сформулированных выше , и других функций , которые кажутся уместными. Это показано на рисунке 1 :
Почему BlogStory и BlogStoryVersion концептуализируются как два разных типа сущностей?
Так как:
BlogStoryVersion экземпляр (то есть, «прошлое» один) всегда имеет значение для UpdatedDateTime собственности, в то время как BlogStory явление (то есть, «настоящий» один) никогда не удерживает его.
Кроме того, сущности этих типов уникально идентифицируются по значениям двух различных наборов свойств: BlogStoryNumber (в случае вхождений BlogStory ) и BlogStoryNumber плюс CreatedDateTime (в случае экземпляров BlogStoryVersion ).
Определение интеграции для информационного моделирования ( IDEF1X ) является высоко рекомендуемые данные моделирования методомкоторый был созданкачестве стандарта в декабре 1993 года США Национальным институтом стандартов и технологий (NIST). Он основан на раннем теоретическом материале авторство единственным виновником в реляционную модели , т.е. д - р Ф. Кодда ; о представлении данных сущности-отношения , разработанном доктором П.П. Ченом ; а также о методике проектирования логических баз данных, созданной Робертом Г. Брауном.
Иллюстративная логическая компоновка SQL-DDL
Затем, основываясь на ранее представленном концептуальном анализе, я объявил схему логического уровня ниже:
-- You should determine which are the most fitting
-- data types and sizes for all your table columns
-- depending on your business context characteristics.
-- Also you should make accurate tests to define the most
-- convenient index strategies at the physical level.
-- As one would expect, you are free to make use of
-- your preferred (or required) naming conventions.
CREATE TABLE UserProfile (
UserId INT NOT NULL,
FirstName CHAR(30) NOT NULL,
LastName CHAR(30) NOT NULL,
BirthDate DATETIME NOT NULL,
GenderCode CHAR(3) NOT NULL,
UserName CHAR(20) NOT NULL,
CreatedDateTime DATETIME NOT NULL,
--
CONSTRAINT UserProfile_PK PRIMARY KEY (UserId),
CONSTRAINT UserProfile_AK1 UNIQUE ( -- Composite ALTERNATE KEY.
FirstName,
LastName,
BirthDate,
GenderCode
),
CONSTRAINT UserProfile_AK2 UNIQUE (UserName) -- ALTERNATE KEY.
);
CREATE TABLE BlogStory (
BlogStoryNumber INT NOT NULL,
Title CHAR(60) NOT NULL,
Content TEXT NOT NULL,
CoverImageName CHAR(30) NOT NULL,
IsActive BIT(1) NOT NULL,
AuthorId INT NOT NULL,
CreatedDateTime DATETIME NOT NULL,
--
CONSTRAINT BlogStory_PK PRIMARY KEY (BlogStoryNumber),
CONSTRAINT BlogStory_AK UNIQUE (Title), -- ALTERNATE KEY.
CONSTRAINT BlogStoryToUserProfile_FK FOREIGN KEY (AuthorId)
REFERENCES UserProfile (UserId)
);
CREATE TABLE BlogStoryVersion (
BlogStoryNumber INT NOT NULL,
CreatedDateTime DATETIME NOT NULL,
Title CHAR(60) NOT NULL,
Content TEXT NOT NULL,
CoverImageName CHAR(30) NOT NULL,
IsActive BIT(1) NOT NULL,
AuthorId INT NOT NULL,
UpdatedDateTime DATETIME NOT NULL,
--
CONSTRAINT BlogStoryVersion_PK PRIMARY KEY (BlogStoryNumber, CreatedDateTime), -- Composite PK.
CONSTRAINT BlogStoryVersionToBlogStory_FK FOREIGN KEY (BlogStoryNumber)
REFERENCES BlogStory (BlogStoryNumber),
CONSTRAINT BlogStoryVersionToUserProfile_FK FOREIGN KEY (AuthorId)
REFERENCES UserProfile (UserId),
CONSTRAINT DatesSuccession_CK CHECK (UpdatedDateTime > CreatedDateTime) --Let us hope that MySQL will finally enforce CHECK constraints in a near future version.
);
Протестировано в этой SQL Fiddle, которая работает на MySQL 5.6.
BlogStory
стол
Как вы можете видеть в демонстрационном проекте, я определил BlogStory
столбец PRIMARY KEY (PK для краткости) с типом данных INT. В связи с этим вам может потребоваться исправить встроенный автоматический процесс, который генерирует и назначает числовое значение для такого столбца при каждой вставке строки. Если вы не возражаете иногда оставлять пропуски в этом наборе значений, вы можете использовать атрибут AUTO_INCREMENT , обычно используемый в средах MySQL.
При вводе всех ваших отдельных BlogStory.CreatedDateTime
точек данных вы можете использовать функцию NOW () , которая возвращает значения даты и времени, которые являются текущими на сервере базы данных в момент операции INSERT. Для меня эта практика определенно более подходит и менее подвержена ошибкам, чем использование внешних процедур.
При условии, что, как обсуждалось в (теперь удаленных) комментариях, вы хотите избежать возможности сохранения BlogStory.Title
повторяющихся значений, вы должны установить ограничение UNIQUE для этого столбца. Из-за того, что данный заголовок может быть общим для нескольких (или даже всех) «прошлых» BlogStoryVersions , тогда для столбца не должно быть установлено УНИКАЛЬНОЕ ограничение BlogStoryVersion.Title
.
Я включил BlogStory.IsActive
столбец типа BIT (1) (хотя TINYINT также может быть использован) на случай, если вам нужно обеспечить «мягкую» или «логическую» функциональность DELETE.
Подробности о BlogStoryVersion
столе
С другой стороны, PK BlogStoryVersion
таблицы состоит из (a) BlogStoryNumber
и (b) столбца с именем, CreatedDateTime
который, конечно, отмечает точный момент, в который BlogStory
строка прошла INSERT.
BlogStoryVersion.BlogStoryNumber
помимо того, что является частью PK, он также ограничен как FOREIGN KEY (FK), который ссылается BlogStory.BlogStoryNumber
на конфигурацию, которая обеспечивает ссылочную целостность между строками этих двух таблиц. В этом отношении реализация автоматической генерации a BlogStoryVersion.BlogStoryNumber
не требуется, поскольку, будучи установленным в качестве FK, значения, ВСТАВЛЕННЫЕ в этот столбец, должны быть «взяты из» тех значений, которые уже включены в соответствующий BlogStory.BlogStoryNumber
аналог.
BlogStoryVersion.UpdatedDateTime
Колонна должна сохранять, как и ожидалось, точка во времени , когда BlogStory
строка была изменена и, как следствие, добавляется в BlogStoryVersion
таблицу. Следовательно, вы можете использовать функцию NOW () и в этой ситуации.
Интервал объяла между BlogStoryVersion.CreatedDateTime
и BlogStoryVersion.UpdatedDateTime
выражает весь Период , в течение которого BlogStory
строка была «присутствует» или «текущий».
Соображения для Version
столбца
Это может быть полезно думать BlogStoryVersion.CreatedDateTime
как столбец , который содержит значение , которое представляет собой отдельную «прошлое» версию о более BlogStory . Я считаю, что это намного выгоднее, чем « VersionId
или» VersionCode
, поскольку это удобнее для пользователя в том смысле, что люди, как правило, лучше знакомы с концепциями времени . Например, авторы или читатели блога могут ссылаться на BlogStoryVersion способом, подобным следующему:
- «Я хочу увидеть специфику Версия в BlogStory идентифицированной Номер
1750
, который был Created на 26 August 2015
в 9:30
».
Автор и редактор Роли: Вывод данных и интерпретация
При таком подходе можно легко различить , кто держит «оригинал» AuthorId
из бетона BlogStory , выбирающий «ранняя» версию определенных BlogStoryId
ИЗ BlogStoryVersion
таблицы в силе применения функции MIN () для BlogStoryVersion.CreatedDateTime
.
Таким образом, каждое BlogStoryVersion.AuthorId
значение, содержащееся во всех строках «более поздних» или «последующих» версий, указывает, естественно, идентификатор автора соответствующей версии под рукой, но можно также сказать, что такое значение в то же время обозначает роль играет привлеченного пользователя в качестве редактора в «оригинальной» версии в виде BlogStory .
Да, данное AuthorId
значение может совместно использоваться несколькими BlogStoryVersion
строками, но на самом деле это часть информации, которая рассказывает что-то очень важное о каждой версии , поэтому повторение упомянутых данных не является проблемой.
Формат столбцов DATETIME
Что касается типа данных DATETIME, да, вы правы: « MySQL извлекает и отображает значения DATETIME в« YYYY-MM-DD HH:MM:SS
формате », но вы можете уверенно вводить соответствующие данные таким образом, и когда вам нужно выполнить запрос, вам просто нужно используйте встроенные функции ДАТА и ВРЕМЯ , чтобы, среди прочего, показать соответствующие значения в соответствующем формате для ваших пользователей. Или вы могли бы, конечно, выполнить этот вид форматирования данных с помощью кода ваших прикладных программ.
Последствия BlogStory
операций ОБНОВЛЕНИЕ
Каждый раз, когда BlogStory
строка переносит UPDATE, вы должны убедиться, что соответствующие значения, которые «присутствовали» до тех пор, пока не произошла модификация, вставляются в BlogStoryVersion
таблицу. Поэтому я настоятельно рекомендую выполнять эти операции в рамках одной КИСЛОТНОЙ СДЕЛКИ, чтобы гарантировать, что они рассматриваются как неделимая единица работы. Вы можете также использовать TRIGGERS, но они, так сказать, делают вещи неопрятными.
Представляя VersionId
или VersionCode
столбец
Если вы решили (из-за деловых обстоятельств или личных предпочтений) включить столбец BlogStory.VersionId
или, BlogStory.VersionCode
чтобы различать BlogStoryVersions , вам следует подумать о следующих возможностях:
A VersionCode
может потребоваться быть УНИКАЛЬНЫМ в (i) всей BlogStory
таблице, а также в (ii) BlogStoryVersion
.
Следовательно, вы должны внедрить тщательно протестированный и полностью надежный метод для генерации и присвоения каждого Code
значения.
Возможно, VersionCode
значения могут повторяться в разных BlogStory
строках, но никогда не дублироваться вместе с одинаковыми BlogStoryNumber
. Например, вы могли бы иметь:
- a BlogStoryNumber
3
- версия83o7c5c
и, одновременно,
- a BlogStoryNumber
86
- Версия83o7c5c
и
- a BlogStoryNumber
958
- Версия83o7c5c
.
Более поздняя возможность открывает другую альтернативу:
Хранение VersionNumber
для BlogStories
, так что там может быть:
- BlogStoryNumber
23
- Версии1, 2, 3…
;
- BlogStoryNumber
650
- Версии1, 2, 3…
;
- BlogStoryNumber
2254
- Версии1, 2, 3…
;
- и т.п.
Хранение «оригинальной» и «последующих» версий в одной таблице
Несмотря на то, сохраняя все BlogStoryVersions в одной отдельной базовой таблице можно, я предлагаю не делать это , потому что вы бы смешение двух различных (концептуальными) типов фактов, которые , таким образом , имеют нежелательные побочные эффекты на
- ограничения данных и манипуляции (на логическом уровне), а также
- соответствующая обработка и хранение (на физическом уровне).
Но при условии, что вы решите следовать этому курсу, вы все равно сможете воспользоваться многими из идей, описанных выше, например:
- композит ПК , состоящий из столбца INT (
BlogStoryNumber
) и столбец DATETIME (CreatedDateTime
);
- использование функций сервера для оптимизации соответствующих процессов, и
- Автор и редактор выводимые Роли .
Видя, что при таком подходе BlogStoryNumber
значение будет продублировано, как только будут добавлены «более новые» версии , и вариант, который вы можете оценить (который очень похож на упомянутые в предыдущем разделе), устанавливает BlogStory
PK состоящий из колонн BlogStoryNumber
и VersionCode
, таким образом , вы могли бы однозначно идентифицировать каждую версию в виде BlogStory . И вы можете попробовать с комбинацией BlogStoryNumber
и VersionNumber
тоже.
Аналогичный сценарий
Вы можете найти мой ответ на этот вопрос помощи, поскольку я также предлагаю включить временные возможности в соответствующей базе данных для решения сопоставимого сценария.