Nie przeprowadziłem formalnych badań, ale z własnego doświadczenia przypuszczam, że ponad 80% błędów projektowych baz danych jest generowanych przez projektowanie z wydajnością jako najważniejszą (jeśli nie jedyną) kwestią.
Jeśli dobry projekt wymaga wielu tabel, utwórz wiele tabel. Nie zakładaj automatycznie, że łączenia są czymś, czego należy unikać. Rzadko są prawdziwą przyczyną problemów z wydajnością.
Podstawowym czynnikiem, przede wszystkim na wszystkich etapach projektowania bazy danych, jest integralność danych. „Odpowiedź może nie zawsze być prawidłowa, ale możemy ją szybko uzyskać” nie jest celem, do którego powinien dążyć żaden sklep. Gdy integralność danych zostanie zablokowana, jeśli wydajność kiedykolwiek stanie się problemem , można się nim zająć. Nie poświęcaj integralności danych, zwłaszcza w celu rozwiązania problemów, które mogą nie istnieć.
Mając to na uwadze, spójrz na to, czego potrzebujesz. Masz obserwacje, które musisz przechowywać. Obserwacje te mogą różnić się liczbą i typami atrybutów i mogą dotyczyć między innymi wartości pomiaru, powiadomienia o zdarzeniu i zmianie statusu oraz z możliwością dodania przyszłych obserwacji.
Wygląda na to, że pasuje to do standardowego wzorca „typ/podtyp”, gdzie wpis „Obserwacja” jest typem, a każdy typ lub rodzaj obserwacji jest podtypem, i sugeruje pewną formę pola wskaźnika typu, taką jak:
create table Observations(
...,
ObservationKind char( 1 ) check( ObservationKind in( 'M', 'E', 'S' )),
...
);
Ale zakodowanie takiej listy na sztywno w ograniczeniu sprawdzającym ma bardzo niski poziom konserwacji. Staje się częścią schematu i można go zmienić tylko za pomocą instrukcji DDL. Nie jest to coś, na co Twój administrator DBA będzie czekał.
Miej więc rodzaje obserwacji we własnej tabeli przeglądowej:
ID Name Meaning
== =========== =======
M Measurement The value of some system metric (CPU_Usage).
E Event An event has been detected.
S Status A change in a status has been detected.
(Pole char może równie dobrze być int lub smallint. Używam char tutaj do ilustracji.)
Następnie wypełnij tabelę Obserwacje PK i atrybutami, które byłyby wspólne dla wszystkich obserwacji.
create table Observations(
ID int identity primary key,
ObservationKind char( 1 ) not null,
DateEntered date not null,
...,
constraint FK_ObservationKind foreign key( ObservationKind )
references ObservationKinds( ID ),
constraint UQ_ObservationIDKind( ID, ObservationKind )
);
Może wydawać się dziwne, aby stworzyć unikalny indeks na kombinacji pola Kind i PK, który jest unikalny sam w sobie, ale wytrzymaj ze mną przez chwilę.
Teraz każdy rodzaj lub podtyp ma swoją własną tabelę. Pamiętaj, że każdy rodzaj obserwacji otrzymuje tabelę, a nie typ danych.
create table Measurements(
ID int not null,
ObservationKind char( 1 ) check( ObservationKind = 'M' ),
Name varchar( 32 ) not null, -- Such as "CPU Usage"
Value double not null, -- such as 55.00
..., -- other attributes of Measurement observations
constraint PK_Measurements primary key( ID, ObservationKind ),
constraint FK_Measurements_Observations foreign key( ID, ObservationKind )
references Observations( ID, ObservationKind )
);
Pierwsze dwa pola będą takie same dla innych rodzajów obserwacji, z wyjątkiem ograniczenia sprawdzającego, które wymusi odpowiedni rodzaj wartości. Pozostałe pola mogą różnić się liczbą, nazwą i typem danych.
Przeanalizujmy przykładową krotkę, która może istnieć w tabeli Pomiary:
ID ObservationKind Name Value ...
==== =============== ========= =====
1001 M CPU Usage 55.0 ...
Aby ta krotka istniała w tej tabeli, pasujący wpis musi najpierw istnieć w tabeli Obserwacje o wartości identyfikatora 1001 i wartości rodzaju obserwacji „M”. Żaden inny wpis o wartości identyfikatora 1001 nie może istnieć ani w tabeli Obserwacje, ani w tabeli Pomiary i nie może istnieć w żadnej innej tabeli „rodzaju” (Zdarzenia, Status). Działa to w ten sam sposób dla wszystkich rodzajów stołów.
Zalecam ponadto utworzenie widoku dla każdego rodzaju obserwacji, który zapewni połączenie każdego rodzaju z główną tabelą obserwacji:
create view MeasurementObservations as
select ...
from Observations o
join Measurements m
on m.ID = o.ID;
Każdy kod, który działa wyłącznie z pomiarami, musiałby trafić tylko w ten widok, a nie w tabele bazowe. Używanie widoków do tworzenia ściany abstrakcji między kodem aplikacji a surowymi danymi znacznie poprawia łatwość utrzymania bazy danych.
Teraz tworzenie innego rodzaju obserwacji, na przykład „Błąd”, obejmuje prostą instrukcję Insert do tabeli ObservationKinds:
F Fault A fault or error has been detected.
Oczywiście musisz utworzyć nową tabelę i widok dla tych obserwacji błędów, ale nie będzie to miało wpływu na istniejące tabele, widoki lub kod aplikacji (poza oczywiście napisaniem nowego kodu do pracy z nowymi obserwacjami) .