@Bill Karwin descreve três modelos de herança em seu livro SQL Antipatterns , ao propor soluções para o antipattern SQL Entity-Attribute-Value . Esta é uma breve visão geral:
Herança de tabela única (também conhecida como tabela por herança de hierarquia):
Usar uma única tabela como na sua primeira opção é provavelmente o design mais simples. Como você mencionou, muitos atributos específicos ao subtipo deverão receber um NULL
valor nas linhas em que esses atributos não se aplicam. Com este modelo, você teria uma tabela de políticas, com a seguinte aparência:
+------+---------------------+----------+----------------+------------------+
| id | date_issued | type | vehicle_reg_no | property_address |
+------+---------------------+----------+----------------+------------------+
| 1 | 2010-08-20 12:00:00 | MOTOR | 01-A-04004 | NULL |
| 2 | 2010-08-20 13:00:00 | MOTOR | 02-B-01010 | NULL |
| 3 | 2010-08-20 14:00:00 | PROPERTY | NULL | Oxford Street |
| 4 | 2010-08-20 15:00:00 | MOTOR | 03-C-02020 | NULL |
+------+---------------------+----------+----------------+------------------+
\------ COMMON FIELDS -------/ \----- SUBTYPE SPECIFIC FIELDS -----/
Manter o design simples é uma vantagem, mas os principais problemas com essa abordagem são os seguintes:
Quando se trata de adicionar novos subtipos, você teria que alterar a tabela para acomodar os atributos que descrevem esses novos objetos. Isso pode se tornar rapidamente problemático quando você tem muitos subtipos ou se planeja adicionar subtipos regularmente.
O banco de dados não poderá impor quais atributos se aplicam e quais não se aplicam, pois não há metadados para definir quais atributos pertencem a quais subtipos.
Você também não pode impor NOT NULL
atributos de um subtipo que devem ser obrigatórios. Você precisaria lidar com isso em seu aplicativo, o que geralmente não é o ideal.
Herança concreta da tabela:
Outra abordagem para lidar com a herança é criar uma nova tabela para cada subtipo, repetindo todos os atributos comuns em cada tabela. Por exemplo:
--// Table: policies_motor
+------+---------------------+----------------+
| id | date_issued | vehicle_reg_no |
+------+---------------------+----------------+
| 1 | 2010-08-20 12:00:00 | 01-A-04004 |
| 2 | 2010-08-20 13:00:00 | 02-B-01010 |
| 3 | 2010-08-20 15:00:00 | 03-C-02020 |
+------+---------------------+----------------+
--// Table: policies_property
+------+---------------------+------------------+
| id | date_issued | property_address |
+------+---------------------+------------------+
| 1 | 2010-08-20 14:00:00 | Oxford Street |
+------+---------------------+------------------+
Esse design basicamente resolverá os problemas identificados para o método de tabela única:
Agora, os atributos obrigatórios podem ser aplicados NOT NULL
.
Adicionar um novo subtipo requer adicionar uma nova tabela em vez de adicionar colunas a uma existente.
Também não há risco de que um atributo inadequado seja definido para um subtipo específico, como o vehicle_reg_no
campo para uma política de propriedade.
Não há necessidade do type
atributo, como no método de tabela única. O tipo agora é definido pelos metadados: o nome da tabela.
No entanto, este modelo também possui algumas desvantagens:
Os atributos comuns são misturados com os atributos específicos do subtipo e não há uma maneira fácil de identificá-los. O banco de dados também não saberá.
Ao definir as tabelas, você teria que repetir os atributos comuns para cada tabela de subtipo. Definitivamente não é SECO .
A pesquisa de todas as políticas, independentemente do subtipo, torna-se difícil e exigiria vários UNION
s.
É assim que você precisaria consultar todas as políticas, independentemente do tipo:
SELECT date_issued, other_common_fields, 'MOTOR' AS type
FROM policies_motor
UNION ALL
SELECT date_issued, other_common_fields, 'PROPERTY' AS type
FROM policies_property;
Observe como a adição de novos subtipos exigiria que a consulta acima fosse modificada com um adicional UNION ALL
para cada subtipo. Isso pode facilmente levar a erros no seu aplicativo se esta operação for esquecida.
Herança de tabela de classe (também conhecida como tabela por herança de tipo):
Esta é a solução que @David menciona na outra resposta . Você cria uma única tabela para sua classe base, que inclui todos os atributos comuns. Em seguida, você criaria tabelas específicas para cada subtipo, cuja chave primária também serve como uma chave estrangeira para a tabela base. Exemplo:
CREATE TABLE policies (
policy_id int,
date_issued datetime,
-- // other common attributes ...
);
CREATE TABLE policy_motor (
policy_id int,
vehicle_reg_no varchar(20),
-- // other attributes specific to motor insurance ...
FOREIGN KEY (policy_id) REFERENCES policies (policy_id)
);
CREATE TABLE policy_property (
policy_id int,
property_address varchar(20),
-- // other attributes specific to property insurance ...
FOREIGN KEY (policy_id) REFERENCES policies (policy_id)
);
Esta solução resolve os problemas identificados nos outros dois projetos:
Atributos obrigatórios podem ser aplicados com NOT NULL
.
Adicionar um novo subtipo requer adicionar uma nova tabela em vez de adicionar colunas a uma existente.
Não há risco de que um atributo inadequado seja definido para um subtipo específico.
Não há necessidade do type
atributo.
Agora, os atributos comuns não são mais misturados aos atributos específicos do subtipo.
Podemos ficar secos, finalmente. Não há necessidade de repetir os atributos comuns para cada tabela de subtipo ao criar as tabelas.
O gerenciamento de um incremento automático id
para as políticas se torna mais fácil, porque isso pode ser tratado pela tabela base, em vez de cada tabela de subtipo gerá-las independentemente.
A pesquisa de todas as políticas, independentemente do subtipo, agora é muito fácil: não UNION
é necessário - apenas a SELECT * FROM policies
.
Considero a abordagem da tabela de classes a mais adequada na maioria das situações.
Os nomes desses três modelos vêm do livro de Martin Fowler, Patterns of Enterprise Application Architecture .