Eu não fiz um estudo formal, mas pela minha própria experiência eu diria que mais de 80% das falhas de design de banco de dados são geradas pelo design com desempenho como a consideração mais importante (se não a única).
Se um bom design exigir várias tabelas, crie várias tabelas. Não assuma automaticamente que as junções são algo a ser evitado. Eles raramente são a verdadeira causa dos problemas de desempenho.
A principal consideração, em primeiro lugar em todos os estágios do projeto de banco de dados, é a integridade dos dados. "A resposta pode nem sempre estar correta, mas podemos entregá-la a você muito rapidamente" não é um objetivo pelo qual qualquer loja deva trabalhar. Depois que a integridade dos dados for bloqueada, se o desempenho se tornar um problema , pode ser abordado. Não sacrifique a integridade dos dados, especialmente para resolver problemas que podem não existir.
Com isso em mente, veja o que você precisa. Você tem observações que precisa armazenar. Essas observações podem variar em número e tipos de atributos e podem ser coisas como o valor de uma medição, a notificação de um evento e a mudança de um status, entre outros e com a possibilidade de adicionar observações futuras.
Isso parece se encaixar em um padrão padrão de "tipo/subtipo", com a entrada "Observação" sendo o tipo e cada tipo ou tipo de observação sendo o subtipo, e sugere alguma forma de campo indicador de tipo, como:
create table Observations(
...,
ObservationKind char( 1 ) check( ObservationKind in( 'M', 'E', 'S' )),
...
);
Mas codificar uma lista como essa em uma restrição de verificação tem um nível de manutenção muito baixo. Ele se torna parte do esquema e pode ser alterado apenas com instruções DDL. Não é algo que seu DBA vai esperar.
Então, tenha os tipos de observações em sua própria tabela de pesquisa:
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.
(O campo char também pode ser int ou smallint. Eu uso char aqui para ilustração.)
Em seguida, preencha a tabela de Observações com um PK e os atributos que seriam comuns a todas as observações.
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 )
);
Pode parecer estranho criar um índice único na combinação do campo Kind e o PK, que é único por si só, mas tenha paciência comigo um momento.
Agora cada tipo ou subtipo recebe sua própria tabela. Observe que cada tipo de observação recebe uma tabela, não o tipo de dados.
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 )
);
Os dois primeiros campos serão os mesmos para os outros tipos de observações, exceto que a restrição de verificação forçará o valor para o tipo apropriado. Os outros campos podem diferir em número, nome e tipo de dados.
Vamos examinar uma tupla de exemplo que pode existir na tabela Medidas:
ID ObservationKind Name Value ...
==== =============== ========= =====
1001 M CPU Usage 55.0 ...
Para que essa tupla exista nesta tabela, uma entrada correspondente deve existir primeiro na tabela Observações com um valor de ID de 1001 e um valor de tipo de observação de 'M'. Nenhuma outra entrada com um valor de ID de 1001 pode existir na tabela Observações ou na tabela Medidas e não pode existir em nenhuma outra das tabelas "tipo" (Eventos, Status). Isso funciona da mesma maneira para todas as tabelas de tipos.
Eu recomendaria ainda criar uma visão para cada tipo de observação que fornecerá uma junção de cada tipo com a tabela de observação principal:
create view MeasurementObservations as
select ...
from Observations o
join Measurements m
on m.ID = o.ID;
Qualquer código que funcione apenas com medições precisaria apenas atingir essa visualização em vez das tabelas subjacentes. O uso de visualizações para criar uma barreira de abstração entre o código do aplicativo e os dados brutos aumenta muito a capacidade de manutenção do banco de dados.
Agora, a criação de outro tipo de observação, como "Error", envolve uma simples instrução Insert na tabela ObservationKinds:
F Fault A fault or error has been detected.
Obviamente, você precisa criar uma nova tabela e visualização para essas observações de erro, mas isso não terá impacto nas tabelas, visualizações ou código do aplicativo existentes (exceto, é claro, para escrever o novo código para trabalhar com as novas observações) .