Analizando el escenario, que presenta características asociadas con el tema conocido como bases de datos temporales , desde una perspectiva conceptual, se puede determinar que: (a) una versión de historia de blog “presente” y (b) una versión de historia de blog “pasada” , aunque muy parecido, son entidades de diferentes tipos.
Además de eso, cuando se trabaja en el nivel lógico de abstracción, los hechos (representados por filas) de distintos tipos deben conservarse en tablas distintas. En el caso bajo consideración, incluso cuando es bastante similar, (i) los hechos sobre las versiones "presentes" son diferentes de (ii) los hechos sobre las versiones "pasadas" .
Por eso recomiendo gestionar la situación mediante dos tablas:
uno dedicado exclusivamente para las versiones "actual" o "presente" de las historias del blog , y
uno que está separado, pero también vinculado con el otro, para todas las versiones "anteriores" o "pasadas" ;
cada uno con (1) un número ligeramente distinto de columnas y (2) un grupo diferente de restricciones.
Volviendo a la capa conceptual, considero que, en su entorno empresarial, Autor y Editor son nociones que se pueden delinear como Roles que puede desempeñar un Usuario , y estos aspectos importantes dependen de la derivación de datos (a través de operaciones de manipulación de nivel lógico) e interpretación (llevada a cabo por los lectores y escritores de Blog Stories , en el nivel externo del sistema de información computarizado, con la asistencia de uno o más programas de aplicación).
Detallaré todos estos factores y otros puntos relevantes de la siguiente manera.
Reglas del negocio
Según tengo entendido de sus requisitos, las siguientes formulaciones de reglas de negocio (juntas en términos de los tipos de entidad relevantes y sus tipos de interrelaciones) son especialmente útiles para establecer el esquema conceptual correspondiente :
- Un usuario escribe cero-uno o muchos BlogStories
- Un BlogStory contiene cero-uno-o-muchos BlogStoryVersions
- Un usuario escribió cero-uno o muchos BlogStoryVersions
Diagrama IDEF1X expositivo
En consecuencia, para exponer mi sugerencia en virtud de un dispositivo gráfico, he creado un IDEF1X de muestra, un diagrama que se deriva de las reglas comerciales formuladas anteriormente y otras características que parecen pertinentes. Se muestra en la Figura 1 :
¿Por qué BlogStory y BlogStoryVersion se conceptualizan como dos tipos de entidades diferentes?
Porque:
Una instancia de BlogStoryVersion (es decir, una "pasada") siempre tiene un valor para una propiedad UpdatedDateTime , mientras que una ocurrencia de BlogStory (es decir, una "presente") nunca lo tiene.
Además, las entidades de esos tipos se identifican de forma exclusiva por los valores de dos conjuntos distintos de propiedades: BlogStoryNumber (en el caso de las ocurrencias de BlogStory ) y BlogStoryNumber más CreatedDateTime (en el caso de las instancias de BlogStoryVersion ).
Una definición de integración para el modelado de información ( IDEF1X ) es una técnica de modelado de datos altamente recomendable que fue establecida como estándar en diciembre de 1993 por el Instituto Nacional de Estándares y Tecnología de los Estados Unidos(NIST). Se basa en el material teórico temprano escrito por el único autor del modelo relacional , es decir, el Dr. EF Codd ; en lavista de datos Entidad-Relación , desarrollada por el Dr. PP Chen ; y también en la técnica de diseño de bases de datos lógicas, creada por Robert G. Brown.
Diseño ilustrativo de SQL-DDL lógico
Luego, con base en el análisis conceptual presentado anteriormente, declaró el diseño de nivel lógico a continuación:
-- 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.
);
Probado en este SQL Fiddle que se ejecuta en MySQL 5.6.
La BlogStory
mesa
Como puede ver en el diseño de demostración, he definido la BlogStory
columna PRIMARY KEY (PK por brevedad) con el tipo de datos INT. En este sentido, es posible que desee corregir un proceso automático incorporado que genera y asigna un valor numérico para dicha columna en cada inserción de fila. Si no le importa dejar huecos ocasionalmente en este conjunto de valores, puede emplear el atributo AUTO_INCREMENT , comúnmente utilizado en entornos MySQL.
Al ingresar todos sus BlogStory.CreatedDateTime
puntos de datos individuales , puede utilizar la función NOW () , que devuelve los valores de fecha y hora actuales en el servidor de la base de datos en el instante exacto de la operación INSERT. Para mí, esta práctica es decididamente más adecuada y menos propensa a errores que el uso de rutinas externas.
Siempre que, como se discute en los comentarios (ahora eliminados), desea evitar la posibilidad de mantener BlogStory.Title
valores duplicados, debe configurar una restricción ÚNICA para esta columna. Debido al hecho de que un Título dado puede ser compartido por varias (o incluso todas) las Versiones BlogStory "pasadas" , entonces no se debe establecer una restricción ÚNICA para la BlogStoryVersion.Title
columna.
Incluí la BlogStory.IsActive
columna de tipo BIT (1) (aunque también se puede usar un TINYINT ) en caso de que necesite proporcionar la funcionalidad DELETE "suave" o "lógica".
Detalles sobre la BlogStoryVersion
mesa
Por otro lado, el PK de la BlogStoryVersion
tabla se compone de (a) BlogStoryNumber
y (b) una columna llamada CreatedDateTime
que, por supuesto, marca el instante preciso en el que una BlogStory
fila se sometió a una INSERT.
BlogStoryVersion.BlogStoryNumber
, además de ser parte de la PK, también está restringido como una CLAVE EXTRANJERA (FK) que hace referencia BlogStory.BlogStoryNumber
, una configuración que impone integridad referencial entre las filas de estas dos tablas. A este respecto, la implementación de una generación automática de a BlogStoryVersion.BlogStoryNumber
no es necesaria porque, al establecerse como FK, los valores INSERTADOS en esta columna deben "extraerse" de los ya incluidos en la BlogStory.BlogStoryNumber
contraparte relacionada .
La BlogStoryVersion.UpdatedDateTime
columna debe retener, como se esperaba, el momento en el que BlogStory
se modificó una fila y, como consecuencia, se agregó a la BlogStoryVersion
tabla. Por lo tanto, también puede usar la función NOW () en esta situación.
El intervalo comprendido entre BlogStoryVersion.CreatedDateTime
y BlogStoryVersion.UpdatedDateTime
expresa todo el período durante el cual una BlogStory
fila estuvo "presente" o "actual".
Consideraciones para una Version
columna
Puede ser útil pensar BlogStoryVersion.CreatedDateTime
en la columna que contiene el valor que representa una versión "pasada" particular de un BlogStory . Considero que esto es mucho más beneficioso que un VersionId
o VersionCode
, ya que es más fácil de usar en el sentido de que las personas tienden a estar más familiarizadas con los conceptos de tiempo . Por ejemplo, los autores o lectores del blog pueden referirse a una BlogStoryVersion de manera similar a la siguiente:
- “Quiero ver lo específico Versión del BlogStory identificado por número
1750
que fue creada en 26 August 2015
a 9:30
”.
El autor y el editor Roles: la derivación de datos e interpretación
Con este enfoque, se puede distinguir fácilmente que ostenta el “original” AuthorId
de un hormigón BlogStory seleccionando la opción “más antiguo” versión de un determinado BlogStoryId
DESDE la BlogStoryVersion
mesa, en virtud de la aplicación de la función MIN () a BlogStoryVersion.CreatedDateTime
.
De esta manera, cada BlogStoryVersion.AuthorId
valor contenido en todas las filas de Versiones "posteriores" o "posteriores" indica, naturalmente, el identificador de Autor de la respectiva Versión en cuestión, pero también se puede decir que dicho valor es, al mismo tiempo, denotar el papel desempeñado por el usuario involucrado como editor de la versión "original" de un BlogStory .
Sí, un AuthorId
valor dado puede ser compartido por varias BlogStoryVersion
filas, pero esta es en realidad una información que dice algo muy significativo sobre cada versión , por lo que la repetición de dicho dato no es un problema.
El formato de las columnas DATETIME
En cuanto al tipo de datos DATETIME, sí, tiene razón, “ MySQL recupera y muestra los valores DATETIME en YYYY-MM-DD HH:MM:SS
formato ' ' ”, pero puede ingresar con confianza los datos pertinentes de esta manera, y cuando tiene que realizar una consulta solo tiene que utilice las funciones DATE y TIME integradas para, entre otras cosas, mostrar los valores correspondientes en el formato apropiado para sus usuarios. O bien, podría llevar a cabo este tipo de formateo de datos a través del código de sus programas de aplicación.
Implicaciones de las BlogStory
operaciones de ACTUALIZACIÓN
Cada vez que una BlogStory
fila sufre una ACTUALIZACIÓN, debe asegurarse de que los valores correspondientes que estaban "presentes" hasta que se realizó la modificación se INSERTEN en la BlogStoryVersion
tabla. Por lo tanto, sugiero realizar estas operaciones dentro de una sola TRANSACCIÓN ÁCIDA para garantizar que sean tratadas como una Unidad de Trabajo indivisible. También puede emplear DISPARADORES, pero tienden a poner las cosas desordenadas, por así decirlo.
Introduciendo una VersionId
o VersionCode
columna
Si opta (debido a circunstancias comerciales o preferencias personales) para incorporar una BlogStory.VersionId
o BlogStory.VersionCode
columna para distinguir las versiones de BlogStory , debe considerar las siguientes posibilidades:
Se VersionCode
podría requerir que A sea ÚNICO en (i) toda la BlogStory
tabla y también en (ii) BlogStoryVersion
.
Por lo tanto, debe implementar un método cuidadosamente probado y totalmente confiable para generar y asignar cada Code
valor.
Tal vez, los VersionCode
valores podrían repetirse en diferentes BlogStory
filas, pero nunca duplicarse junto con el mismo BlogStoryNumber
. Por ejemplo, podrías tener:
- un BlogStoryNumber
3
- Versión83o7c5c
y, simultáneamente,
- un BlogStoryNumber
86
- Versión83o7c5c
y
- un BlogStoryNumber
958
- Versión83o7c5c
.
La posibilidad posterior abre otra alternativa:
Manteniendo un VersionNumber
para el BlogStories
, por lo que podría haber:
- BlogStoryNumber
23
- Versiones1, 2, 3…
;
- BlogStoryNumber
650
- Versiones1, 2, 3…
;
- BlogStoryNumber
2254
- Versiones1, 2, 3…
;
- etc.
Mantener versiones "originales" y "posteriores" en una sola tabla
Aunque es posible mantener todas las versiones de BlogStory en la misma tabla base individual , sugiero no hacerlo porque estaría mezclando dos tipos distintos (conceptuales) de hechos, lo que tiene efectos secundarios indeseables en
- restricciones y manipulación de datos (a nivel lógico), junto con
- el procesamiento y almacenamiento relacionados (en el nivel físico).
Pero, a condición de que elija seguir ese curso de acción, aún puede aprovechar muchas de las ideas detalladas anteriormente, por ejemplo:
- un compuesto PK que consiste en una columna INT (
BlogStoryNumber
) y una columna DATETIME ( CreatedDateTime
);
- el uso de las funciones del servidor para optimizar los procesos pertinentes, y
- el autor y el editor Roles derivables .
Al ver que, al proceder con este enfoque, BlogStoryNumber
se duplicará un valor tan pronto como se agreguen las versiones "más nuevas" , una opción que y que podría evaluar (que es muy similar a las mencionadas en la sección anterior) está estableciendo un BlogStory
PK compuesto por las columnas BlogStoryNumber
y VersionCode
, de esta manera, podrá identificar de forma única cada versión de un BlogStory . Y puedes probar con una combinación de BlogStoryNumber
y VersionNumber
también.
Escenario similar
Puede encontrar mi respuesta a esta pregunta de ayuda, ya que yo también propongo habilitar capacidades temporales en la base de datos correspondiente para hacer frente a un escenario comparable.