Analizzando lo scenario - che presenta le caratteristiche associate all'argomento noto come database temporali - da una prospettiva concettuale, si può determinare che: (a) una versione "attuale" di Blog Story e (b) una versione "passato" di Blog Story , sebbene molto simili, sono entità di diversi tipi.
Inoltre, quando si lavora a livello logico di astrazione, i fatti (rappresentati da righe) di tipi distinti devono essere conservati in tabelle distinte. Nel caso in esame, anche se abbastanza simili, (i) i fatti relativi alle versioni "attuali" sono diversi dai (ii) fatti relativi alle versioni "passate" .
Pertanto raccomando di gestire la situazione mediante due tabelle:
uno dedicato esclusivamente alle versioni "attuali" o "presenti" delle storie del blog , e
uno che è separato, ma anche collegato con l'altro, per tutte le versioni "precedenti" o "passate" ;
ciascuno con (1) un numero leggermente distinto di colonne e (2) un diverso gruppo di vincoli.
Tornando al livello concettuale, ritengo che, nel tuo ambiente aziendale, Autore ed Editor siano nozioni che possono essere delineate come Ruoli che possono essere riprodotti da un Utente e questi aspetti importanti dipendono dalla derivazione dei dati (tramite operazioni di manipolazione a livello logico) e interpretazione (effettuata dai lettori e scrittori di Blog Stories , a livello esterno del sistema informativo computerizzato, con l'assistenza di uno o più programmi applicativi).
Descriverò in dettaglio tutti questi fattori e altri punti rilevanti come segue.
Regole di business
Secondo la mia comprensione dei vostri requisiti, le seguenti formulazioni di regole commerciali (messe insieme in termini di tipi di entità rilevanti e dei loro tipi di interrelazioni) sono particolarmente utili per stabilire lo schema concettuale corrispondente :
- Un utente scrive zero-one-o-many BlogStories
- Un BlogStory contiene zero-uno-o-molti BlogStoryVersions
- Un utente ha scritto zero-uno-o-molti BlogStoryVersions
Diagramma IDEF1X dell'esposizione
Di conseguenza, al fine di esporre il mio suggerimento in virtù di un dispositivo grafico, ho creato un IDEF1X di esempio un diagramma derivato dalle regole aziendali sopra formulate e altre caratteristiche che sembrano pertinenti. È mostrato in Figura 1 :
Perché BlogStory e BlogStoryVersion sono concettualizzati come due diversi tipi di entità?
Perché:
Un BlogStoryVersion esempio (cioè, uno “passato”) contiene sempre un valore per un UpdatedDateTime struttura, mentre una BlogStory evento (cioè, un “regalo” uno) non tiene.
Inoltre, le entità di questi tipi sono identificate in modo univoco dai valori di due distinti insiemi di proprietà: BlogStoryNumber (nel caso delle occorrenze BlogStory ) e BlogStoryNumber più CreatedDateTime (nel caso delle istanze BlogStoryVersion ).
una Integration Definition for Information Modeling ( IDEF1X ) è una tecnica di modellazione dei dati altamente raccomandabile che è stata stabilita come standard nel dicembre 1993 dal National Institute of Standards and Technology (NIST)degli Stati Uniti. Si basa sul primo materiale teorico creato dall'unico creatore del modello relazionale , cioè il dott. EF Codd ; sullavisione Entity-Relationship dei dati, sviluppata dal Dr. PP Chen ; e anche sulla tecnica di progettazione del database logico, creata da Robert G. Brown.
Layout logico SQL-DDL illustrativo
Quindi, in base all'analisi concettuale precedentemente presentata, ho dichiarato il progetto a livello logico di seguito:
-- 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.
);
Testato in questo SQL Fiddle che funziona su MySQL 5.6.
Il BlogStory
tavolo
Come puoi vedere nella progettazione demo, ho definito la BlogStory
colonna PRIMARY KEY (PK per brevità) con il tipo di dati INT. A questo proposito, potresti voler correggere un processo automatico incorporato che genera e assegna un valore numerico per tale colonna in ogni inserimento di riga. Se non ti dispiace lasciare occasionalmente delle lacune in questo insieme di valori, puoi utilizzare l' attributo AUTO_INCREMENT , comunemente usato negli ambienti MySQL.
Quando si inseriscono tutti i singoli BlogStory.CreatedDateTime
punti dati, è possibile utilizzare la funzione NOW () , che restituisce i valori di data e ora correnti nel server di database nell'istante esatto dell'operazione INSERT. Per me, questa pratica è decisamente più adatta e meno soggetta a errori rispetto all'uso di routine esterne.
A condizione che, come discusso nei commenti (ora rimossi), si desidera evitare la possibilità di mantenere BlogStory.Title
valori duplicati, è necessario impostare un vincolo UNIQUE per questa colonna. A causa del fatto che un determinato titolo può essere condiviso da più (o anche da tutte) le "precedenti" BlogStoryVersions , non è necessario stabilire un vincolo UNICO per la BlogStoryVersion.Title
colonna.
Ho incluso la BlogStory.IsActive
colonna di tipo BIT (1) (anche se può essere usato anche un TINYINT ) nel caso in cui sia necessario fornire funzionalità DELETE "soft" o "logico".
Dettagli sul BlogStoryVersion
tavolo
D'altra parte, il PK della BlogStoryVersion
tabella è composto da (a) BlogStoryNumber
e (b) una colonna denominata CreatedDateTime
che, ovviamente, segna il preciso istante in cui una BlogStory
riga ha subito un INSERTO.
BlogStoryVersion.BlogStoryNumber
, oltre a far parte del PK, è anche vincolato come FOREIGN KEY (FK) che fa riferimento BlogStory.BlogStoryNumber
, una configurazione che impone l' integrità referenziale tra le righe di queste due tabelle. A questo proposito, BlogStoryVersion.BlogStoryNumber
non è necessaria l' implementazione di una generazione automatica di a perché, essendo impostati come FK, i valori INSERITI in questa colonna devono essere "estratti da" quelli già racchiusi nella relativa BlogStory.BlogStoryNumber
controparte.
La BlogStoryVersion.UpdatedDateTime
colonna dovrebbe conservare, come previsto, il momento in cui una BlogStory
riga è stata modificata e, di conseguenza, aggiunta alla BlogStoryVersion
tabella. Quindi, puoi usare la funzione NOW () anche in questa situazione.
L' intervallo compreso tra BlogStoryVersion.CreatedDateTime
ed BlogStoryVersion.UpdatedDateTime
esprime l'intero Periodo durante il quale una BlogStory
riga era "presente" o "corrente".
Considerazioni per una Version
colonna
Può essere utile pensare BlogStoryVersion.CreatedDateTime
come la colonna che contiene il valore che rappresenta un particolare “passato” versione di un BlogStory . Lo ritengo molto più vantaggioso di un VersionId
o VersionCode
, poiché è più user-friendly, nel senso che le persone tendono ad avere più familiarità con i concetti del tempo . Ad esempio, gli autori o i lettori del blog potrebbero fare riferimento a BlogStoryVersion in un modo simile al seguente:
- “Voglio vedere la specifica versione del BlogStory identificato da numero
1750
che è stato Creato su 26 August 2015
a 9:30
”.
L' autore e Editor Ruoli: derivazione dei dati e l'interpretazione
Con questo approccio, si può facilmente distinguere chi detiene il “originale” AuthorId
di un calcestruzzo BlogStory selezionando il “prima” versione di un certo BlogStoryId
FROM il BlogStoryVersion
tavolo in virtù dell'applicazione della funzione di MIN () a BlogStoryVersion.CreatedDateTime
.
In questo modo, ogni BlogStoryVersion.AuthorId
valore contenuto in tutte le righe delle versioni "successive" o "successive" indica, naturalmente, l' identificatore dell'autore della rispettiva versione a portata di mano, ma si può anche dire che tale valore è, allo stesso tempo, denotante il ruolo svolto dalla coinvolto utente come redattore della “originale” versione di un BlogStory .
Sì, un determinato AuthorId
valore può essere condiviso da più BlogStoryVersion
righe, ma in realtà si tratta di un'informazione che dice qualcosa di molto significativo su ciascuna versione , quindi la ripetizione di detto dato non è un problema.
Il formato delle colonne DATETIME
Per quanto riguarda il tipo di dati DATETIME, sì, hai ragione, " MySQL recupera e visualizza i valori DATETIME in" YYYY-MM-DD HH:MM:SS
formato ", ma puoi inserire con sicurezza i dati pertinenti in questo modo e quando devi eseguire una query devi solo utilizzare le funzioni DATE e TIME integrate per mostrare, tra le altre cose, i valori relativi nel formato appropriato per gli utenti. Oppure potresti certamente eseguire questo tipo di formattazione dei dati tramite il codice dei tuoi programmi applicativi.
Implicazioni delle BlogStory
operazioni di AGGIORNAMENTO
Ogni volta che una BlogStory
riga subisce un AGGIORNAMENTO, tu necessario assicurarsi che i valori corrispondenti che erano "presenti" fino a quando non ha avuto luogo la modifica vengano quindi INSERITI nella BlogStoryVersion
tabella. Pertanto, consiglio vivamente di compiere queste operazioni all'interno di una singola TRANSAZIONE ACIDA per garantire che siano trattate come un'unità di lavoro indivisibile. Puoi anche impiegare TRIGGERS, ma tendono a rendere le cose disordinate, per così dire.
Presentazione di a VersionId
o VersionCode
column
Se si sceglie (a causa di circostanze aziendali o preferenze personali) di incorporare una BlogStory.VersionId
o una BlogStory.VersionCode
colonna per distinguere BlogStoryVersions , è necessario ponderare le seguenti possibilità:
UN VersionCode
potrebbe essere richiesto di essere UNICO in (i) l'intera BlogStory
tabella e anche in (ii) BlogStoryVersion
.
Pertanto, è necessario implementare un metodo accuratamente testato e totalmente affidabile al fine di generare e assegnare ciascunoCode
valore.
Forse, i VersionCode
valori potrebbero essere ripetuti in BlogStory
righe diverse , ma mai duplicati insieme allo stesso BlogStoryNumber
. Ad esempio, potresti avere:
- un BlogStoryNumber
3
- Versione83o7c5c
e, contemporaneamente,
- un BlogStoryNumber
86
- Versione83o7c5c
e
- un BlogStoryNumber
958
- Versione83o7c5c
.
La possibilità successiva apre un'altra alternativa:
Mantenere un VersionNumber
per ilBlogStories
, quindi potrebbero esserci:
- BlogStoryNumber
23
- Versioni1, 2, 3…
;
- BlogStoryNumber
650
- Versioni1, 2, 3…
;
- BlogStoryNumber
2254
- Versioni1, 2, 3…
;
- eccetera.
Tenendo le versioni "originali" e "successive" in una singola tabella
Sebbene sia possibile mantenere tutte le BlogStoryVersions nella stessa tabella di base individuale , suggerisco di non farlo perché si mescolerebbero due tipi distinti (concettuali) di fatti, che quindi hanno effetti collaterali indesiderati
- vincoli e manipolazione dei dati (a livello logico), insieme a
- la relativa elaborazione e archiviazione (a livello fisico).
Ma, a condizione che tu scelga di seguire quel corso d'azione, puoi comunque trarre vantaggio da molte delle idee sopra descritte, ad esempio:
- un PK composito costituito da una colonna INT (
BlogStoryNumber
) e una colonna DATETIME ( CreatedDateTime
);
- l'utilizzo delle funzioni del server al fine di ottimizzare i processi pertinenti e
- l' autore e Editor derivabili ruoli .
Visto che, procedendo con tale approccio, un BlogStoryNumber
valore verrà duplicato non appena verranno aggiunte versioni “più recenti” , un'opzione che e che potresti valutare (che è molto simile a quelle menzionate nella sezione precedente) sta stabilendo un BlogStory
PK composto dalle colonne BlogStoryNumber
e VersionCode
, in questo modo, saresti in grado di identificare in modo univoco ogni versione di una BlogStory . E puoi provare con una combinazione di BlogStoryNumber
eVersionNumber
anche.
Scenario simile
Potresti trovare la mia risposta a questa domanda di aiuto, poiché anche io propongo di abilitare le capacità temporali nel database in questione per affrontare uno scenario comparabile.