Не съм правил официално проучване, но от собствения си опит предполагам, че повече от 80% от недостатъците в дизайна на база данни се генерират от проектиране, като производителността е най-важното (ако не само) съображение.
Ако добрият дизайн изисква множество таблици, създайте няколко таблици. Не приемайте автоматично, че присъединяванията са нещо, което трябва да се избягва. Те рядко са истинската причина за проблеми с производителността.
Основното съображение, първо и преди всичко във всички етапи на проектиране на база данни, е целостта на данните. „Отговорът може да не винаги е правилен, но можем да ви го предоставим много бързо“ не е цел, към която всеки магазин трябва да работи. След като целостта на данните е заключена, ако производителността някога стане проблем , може да се адресира. Не жертвайте целостта на данните, особено за решаване на проблеми, които може да не съществуват.
Имайки това предвид, вижте какво ви трябва. Имате наблюдения, които трябва да съхраните. Тези наблюдения могат да се различават по броя и видовете атрибути и могат да бъдат неща като стойността на измерване, уведомяването за събитие и промяната на състоянието, наред с други и с възможност за добавяне на бъдещи наблюдения.
Това изглежда се вписва в стандартен модел "тип/подтип", като записът "Наблюдение" е типът, а всеки тип или вид наблюдение е подтип и предлага някаква форма на поле за индикатор за тип, като например:
create table Observations(
...,
ObservationKind char( 1 ) check( ObservationKind in( 'M', 'E', 'S' )),
...
);
Но твърдото кодиране на списък като този в ограничение за проверка има много ниско ниво на поддръжка. Той става част от схемата и може да бъде променен само с DDL изрази. Не е нещо, което вашият DBA ще очаква с нетърпение.
Така че имайте видовете наблюдения в собствената си таблица за търсене:
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.
(Полето char може също да бъде int или smallint. Използвам char тук за илюстрация.)
След това попълнете таблицата за наблюдения с PK и атрибутите, които биха били общи за всички наблюдения.
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 )
);
Може да изглежда странно да се създаде уникален индекс върху комбинацията от полето Kind и PK, който е уникален сам по себе си, но потърпете малко.
Сега всеки вид или подтип получава своя собствена таблица. Имайте предвид, че всеки вид наблюдение получава таблица, а не типа данни.
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 )
);
Първите две полета ще бъдат еднакви за другите видове наблюдения, освен че ограничението за проверка ще принуди стойността към съответния вид. Другите полета може да се различават по брой, име и тип данни.
Нека разгледаме примерен кортеж, който може да съществува в таблицата с измервания:
ID ObservationKind Name Value ...
==== =============== ========= =====
1001 M CPU Usage 55.0 ...
За да може този кортеж да съществува в тази таблица, първо трябва да съществува съвпадащ запис в таблицата „Наблюдения“ със стойност на идентификатор 1001 и стойност на вид наблюдение „M“. Никакъв друг запис със стойност на идентификатор 1001 не може да съществува нито в таблицата за наблюдения, нито в таблицата с измервания и изобщо не може да съществува в която и да е друга от таблиците "вид" (събития, състояние). Това работи по същия начин за всички видове маси.
Освен това бих препоръчал да създадете изглед за всеки вид наблюдение, което ще осигури свързване на всеки вид с основната таблица за наблюдение:
create view MeasurementObservations as
select ...
from Observations o
join Measurements m
on m.ID = o.ID;
Всеки код, който работи само с измервания, ще трябва да удари само този изглед вместо основните таблици. Използването на изгледи за създаване на стена от абстракция между кода на приложението и необработените данни значително подобрява поддръжката на базата данни.
Сега създаването на друг вид наблюдение, като "Грешка", включва прост оператор Insert към таблицата ObservationKinds:
F Fault A fault or error has been detected.
Разбира се, трябва да създадете нова таблица и изглед за тези наблюдения на грешки, но това няма да окаже влияние върху съществуващите таблици, изгледи или код на приложението (освен, разбира се, да напишете новия код за работа с новите наблюдения) .