Analizując scenariusz - który prezentuje cechy związane z tematem zwanym tymczasowymi bazami danych - z perspektywy koncepcyjnej można stwierdzić, że: (a) „obecna” wersja historii blogu i (b) „poprzednia” wersja historii blogu , chociaż bardzo przypominające, są bytami różnych typów.
Ponadto, pracując na logicznym poziomie abstrakcji, fakty (reprezentowane przez rzędy) różnych rodzajów muszą być przechowywane w odrębnych tabelach. W omawianym przypadku, nawet jeśli dość podobne, (i) fakty na temat „obecnych” wersji różnią się od (ii) faktów na temat „przeszłych” wersji .
Dlatego zalecam zarządzanie sytuacją za pomocą dwóch tabel:
jeden dedykowany wyłącznie dla „aktualnych” lub „obecnych” wersji historii blogów , oraz
jeden, który jest odrębny, ale także powiązany z drugim, dla wszystkich „poprzednich” lub „przeszłych” wersji ;
każda z (1) nieco inną liczbą kolumn i (2) inną grupą ograniczeń.
Powrót na warstwie pojęciowej, uważam, że -w Twoja firma środowiska eksploatacji Autor i redaktor są pojęcia, które mogą być wykreślone jako ról , które mogą być odtwarzane przez użytkownika , a te ważne aspekty zależą od danych wyprowadzenia (poprzez operacje logiczne manipulacji szczebla) oraz interpretacja (przeprowadzana przez czytelników i pisarzy Blog Stories , na poziomie zewnętrznym skomputeryzowanego systemu informacyjnego, przy pomocy jednego lub więcej programów aplikacyjnych).
Szczegółowo opiszę wszystkie te czynniki i inne istotne punkty w następujący sposób.
Zasady biznesowe
Zgodnie z moim rozumieniem twoich wymagań, następujące formuły reguł biznesowych (zestawione pod względem odpowiednich typów podmiotów i ich rodzajów wzajemnych powiązań) są szczególnie pomocne w ustanowieniu odpowiedniego schematu pojęciowego :
- Użytkownik pisze zero-jeden-lub-wiele BlogStories
- BlogStory posiada zero-jeden-lub-wiele BlogStoryVersions
- Użytkownik napisał zero-jedynkowa-lub-wiele BlogStoryVersions
Schemat IDEF1X dla ekspozytora
W związku z tym, aby objaśniać moją sugestię dzięki urządzeniu graficznym Stworzyłem próbkę IDEF1X diagram, który wywodzi się z regułami biznesowymi sformułowanych powyżej i innych cech, które wydają się istotne. Pokazano to na rysunku 1 :
Dlaczego BlogStory i BlogStoryVersion są konceptualizowane jako dwa różne typy jednostek?
Bo:
BlogStoryVersion instancji (tj „przeszłości” jeden) zawsze posiada wartość dla UpdatedDateTime nieruchomości, podczas gdy BlogStory występowanie (tj „obecny” jeden) nie trzyma go.
Poza tym encje tych typów są jednoznacznie identyfikowane przez wartości dwóch różnych zestawów właściwości: BlogStoryNumber (w przypadku wystąpień BlogStory ) i BlogStoryNumber plus CreatedDateTime (w przypadku instancji BlogStoryVersion ).
Definicja Integration for Information Modeling ( IDEF1X ) jest dane godne polecenia modelowania techniką, która powstała jako standardu w grudniu 1993 przez United States National Institute of Standards and Technology (NIST). Opiera się ona na początku materiału teoretycznego autorem przez jedynego twórcy tego modelu relacyjnego , czyli dr EF Codd ; wwidoku danych Entity-Relationship danych opracowanym przez dr PP Chen ; a także na temat techniki projektowania logicznej bazy danych, stworzonej przez Roberta G. Browna.
Ilustracyjny logiczny układ SQL-DDL
Następnie, na podstawie poprzednio przedstawionej analizy pojęciowej, zadeklarowałem projekt poziomu logicznego poniżej:
-- 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.
);
Testowany w tym SQL Fiddle, który działa na MySQL 5.6.
BlogStory
stół
Jak widać w projekcie demonstracyjnym, zdefiniowałem BlogStory
kolumnę KLUCZ PODSTAWOWY (PK dla zwięzłości) z typem danych INT. W związku z tym możesz chcieć naprawić wbudowany automatyczny proces, który generuje i przypisuje wartość liczbową dla takiej kolumny w każdym wstawieniu wiersza. Jeśli nie masz nic przeciwko pozostawianiu luk w tym zestawie wartości, możesz zastosować atrybut AUTO_INCREMENT , powszechnie używany w środowiskach MySQL.
Wprowadzając wszystkie swoje indywidualne BlogStory.CreatedDateTime
punkty danych, możesz skorzystać z funkcji NOW () , która zwraca wartości Daty i Czasu , które są aktualne na serwerze bazy danych w momencie wykonania operacji INSERT. Dla mnie ta praktyka jest zdecydowanie bardziej odpowiednia i mniej podatna na błędy niż stosowanie procedur zewnętrznych.
Pod warunkiem, że, jak omówiono w (teraz usuniętych) komentarzach, chcesz uniknąć możliwości utrzymywania BlogStory.Title
zduplikowanych wartości, musisz skonfigurować ograniczenie UNIQUE dla tej kolumny. Z uwagi na fakt, że dany tytuł może być współużytkowany przez kilka (lub nawet wszystkie) „przeszłe” wersje BlogStoryVersions , wówczas dla kolumny nie należy ustanawiać ograniczenia UNIKALNEGO BlogStoryVersion.Title
.
I obejmował BlogStory.IsActive
kolumnę typu BIT (1) (choć TINYINT mogą być również stosowane) w przypadku trzeba zapewnić „miękkie” lub „logiczne” funkcjonalność usunąć.
Szczegóły dotyczące BlogStoryVersion
tabeli
Z drugiej strony, PK BlogStoryVersion
tabeli składa się z (a) BlogStoryNumber
i (b) kolumny o nazwie, CreatedDateTime
która oczywiście oznacza dokładny moment, w którym BlogStory
wiersz przeszedł INSERT.
BlogStoryVersion.BlogStoryNumber
, oprócz tego, że jest częścią PK, jest również ograniczony jako KLUCZ ZAGRANICZNY (FK), który odwołuje się BlogStory.BlogStoryNumber
, konfiguracja, która wymusza integralność referencyjną między wierszami tych dwóch tabel. W związku z tym wdrożenie automatycznego generowania a BlogStoryVersion.BlogStoryNumber
nie jest konieczne, ponieważ ustawione jako FK wartości WSTAWIONE w tej kolumnie muszą być „zaczerpnięte” z wartości już zawartych w powiązanym BlogStory.BlogStoryNumber
odpowiedniku.
BlogStoryVersion.UpdatedDateTime
Kolumnie należy zachować, jak oczekiwano, punkt w czasie, gdy BlogStory
rząd został zmodyfikowany, a w związku z tym dodaje do BlogStoryVersion
tabeli. Dlatego w tej sytuacji możesz również użyć funkcji NOW ().
Odstęp zrozumiana pomiędzy BlogStoryVersion.CreatedDateTime
i BlogStoryVersion.UpdatedDateTime
wyraża całą Okres , podczas którego BlogStory
rząd był „obecny” lub „bieżący”.
Uwagi dotyczące Version
kolumny
Może to być przydatne, aby myśleć o BlogStoryVersion.CreatedDateTime
jak kolumna, która posiada wartość, która stanowi szczególną „przeszłości” Wersja o BlogStory . Uważam, że jest to o wiele bardziej korzystne niż a, VersionId
lub VersionCode
, ponieważ jest bardziej przyjazne dla użytkownika w tym sensie, że ludzie są bardziej zaznajomieni z koncepcjami czasu . Na przykład autorzy lub czytelnicy blogu mogą odnosić się do BlogStoryVersion w sposób podobny do następującego:
- „Chcę zobaczyć specyficznej wersji z BlogStory zidentyfikowanych przez numer
1750
, który został Created na 26 August 2015
co 9:30
”.
Autor i Editor Role: wyprowadzenie danych i interpretacja
Dzięki takiemu podejściu, można łatwo odróżnić, kto trzyma „oryginał” AuthorId
z betonu BlogStory wybierając opcję „najwcześniejszy” Wersja pewnej BlogStoryId
od BlogStoryVersion
stołu dzięki zastosowaniu funkcji MIN () do BlogStoryVersion.CreatedDateTime
.
W ten sposób każda BlogStoryVersion.AuthorId
wartość zawarta we wszystkich wierszach „późniejszych” lub „następnych” wersji wskazuje oczywiście identyfikator autora odpowiedniej wersji , ale można również powiedzieć, że taka wartość oznacza jednocześnie Rola odgrywana przez zaangażowanych użytkownika jako edytora w „oryginalnej” wersji z BlogStory .
Tak, dana AuthorId
wartość może być współużytkowana przez wiele BlogStoryVersion
wierszy, ale w rzeczywistości jest to informacja, która mówi coś bardzo ważnego o każdej wersji , więc powtórzenie wspomnianego układu odniesienia nie stanowi problemu.
Format kolumn DATETIME
Jeśli chodzi o typ danych DATETIME, tak, masz rację, „ MySQL pobiera i wyświetla wartości DATETIME w YYYY-MM-DD HH:MM:SS
formacie „ ”, ale możesz z pewnością wprowadzić odpowiednie dane w ten sposób, a kiedy musisz wykonać zapytanie, musisz tylko skorzystaj z wbudowanych funkcji DATE i TIME , aby między innymi pokazać odpowiednie wartości w odpowiednim formacie dla użytkowników. Lub z pewnością możesz przeprowadzić tego rodzaju formatowanie danych za pomocą kodu aplikacji.
Konsekwencje BlogStory
operacji UPDATE
Za każdym razem, gdy BlogStory
wiersz ma aktualizację, musisz upewnić się, że odpowiednie wartości, które były „obecne” do czasu modyfikacji, zostały następnie WSTAWIONE do BlogStoryVersion
tabeli. Dlatego wysoce sugeruję wykonanie tych operacji w ramach jednej TRANSAKCJI KWASU, aby zagwarantować, że są one traktowane jako niepodzielna Jednostka Pracy. Równie dobrze możesz zatrudnić SPUSTY, ale mają tendencję do robienia rzeczy niechlujnych, że tak powiem.
Przedstawiamy kolumnę VersionId
lubVersionCode
Jeśli zdecydujesz się (ze względu na okoliczności biznesowe lub osobiste preferencje) dołączyć kolumnę BlogStory.VersionId
lub, BlogStory.VersionCode
aby wyróżnić BlogStoryVersions , powinieneś rozważyć następujące możliwości:
VersionCode
Może być wymagane, aby być unikalny dla wzoru (I) w całym BlogStory
stole, a także (ii) BlogStoryVersion
.
Dlatego musisz wdrożyć dokładnie przetestowaną i całkowicie niezawodną metodę, aby wygenerować i przypisać każdą Code
wartość.
Być może VersionCode
wartości mogą być powtarzane w różnych BlogStory
wierszach, ale nigdy nie są powielane wraz z tym samym BlogStoryNumber
. Np. Możesz mieć:
- BlogStoryNumber
3
- Wersja83o7c5c
, a jednocześnie
- BlogStoryNumber
86
- Wersja83o7c5c
i
- BlogStoryNumber
958
- Wersja83o7c5c
.
Późniejsza możliwość otwiera kolejną alternatywę:
Utrzymanie VersionNumber
dla BlogStories
, więc mogą być:
- BlogStoryNumber
23
- wersje1, 2, 3…
;
- BlogStoryNumber
650
- wersje1, 2, 3…
;
- BlogStoryNumber
2254
- wersje1, 2, 3…
;
- itp.
Trzymanie „oryginalnej” i „kolejnej” wersji w jednym stole
Chociaż możliwe jest utrzymanie wszystkich BlogStoryVersions w tej samej indywidualnej tabeli bazowej , sugeruję, aby tego nie robić, ponieważ mieszalibyście dwa różne (pojęciowe) fakty, co w ten sposób ma niepożądane skutki uboczne dla
- ograniczenia danych i manipulacje (na poziomie logicznym), wraz z
- powiązane przetwarzanie i przechowywanie (w warstwie fizycznej).
Ale pod warunkiem, że zdecydujesz się na taki sposób postępowania, możesz nadal korzystać z wielu pomysłów wyszczególnionych powyżej, np .:
- kompozytu PK, składający się z kolumny (INT
BlogStoryNumber
), a kolumnę DATETIME ( CreatedDateTime
);
- wykorzystanie funkcji serwera w celu optymalizacji odpowiednich procesów oraz
- Autor i Editor wywodzących Role .
Widząc, że postępując zgodnie z takim podejściem, BlogStoryNumber
wartość zostanie zduplikowana, gdy tylko zostaną dodane „nowsze” wersje , opcją, którą i którą możesz ocenić (która jest bardzo podobna do tych wymienionych w poprzedniej sekcji) jest ustanowienie BlogStory
PK składa się z kolumn BlogStoryNumber
i VersionCode
w ten sposób będzie można jednoznacznie zidentyfikować każdą wersję o BlogStory . I możesz spróbować z kombinacją BlogStoryNumber
i VersionNumber
też.
Podobny scenariusz
Możesz znaleźć moją odpowiedź na to pytanie pomocy, ponieważ ja również proponuję włączenie możliwości czasowych w odpowiedniej bazie danych, aby poradzić sobie z porównywalnym scenariuszem.
author_id
wartość pola zapytania jest powtarzana w każdym wierszu tabeli. Gdzie i jak powinienem to zatrzymać ?