Na medida em que esta pergunta é uma continuação de, minha implementação do padrão de design de tipo / subtipo (para subclasses mutuamente exclusivas) está correta? , que é uma continuação de Não sei como transformar entidade variável em tabela relacional , eu perguntaria: o que exatamente você está tentando otimizar? Armazenamento? O modelo de objeto? Questão de complexidade? Query performance? Existem trade-offs na otimização de um aspecto versus outro, pois você não pode otimizar todos os aspectos ao mesmo tempo.
Concordo plenamente com os pontos de Remus em relação a:
- Existem prós e contras em cada abordagem (ou seja, o sempre presente fator "depende") e
- A primeira prioridade é a eficiência do modelo de dados (um modelo de dados ineficiente não pode ser corrigido por um código de aplicativo limpo e / ou eficiente)
Dito isto, a escolha que você enfrenta é entre os seguintes, organizados em ordem de menor normalização para mais normalização:
- promovendo propriedade
E
para a tabela de tipo base
- mantendo-o em várias tabelas de subtipo
- normalizar totalmente
E
para uma nova tabela de subclasses intermediária no mesmo nível que C
, A
e B
será diretamente subclasses de ( resposta da @ MDCCL )
Vejamos cada opção:
Mover propriedade E
para a tabela de tipo base
PROs
- Complexidade consulta reduzida para consultas que necessidade
E
, mas não X
, Y
ou Z
.
- Potencialmente mais eficiente para consultas que precisam
E
, mas não X
, Y
ou Z
(consultas especialmente agregados) devido a não JOIN.
- Potencial para criar um índice
(D, E)
(e, em caso afirmativo, potencialmente, um Índice Filtrado em (D, E)
que EntityType <> C
, se tal condição for permitida)
CONs
- Não pode marcar
E
comoNOT NULL
- Precisa de mais
CHECK CONSTRAINT
na tabela do tipo base para garantir que, E IS NULL
quando EntityType = C
(embora esse não seja um grande problema)
- É necessário educar os usuários do modelo de dados sobre o porquê
E
deve ser NULL
e deve ser totalmente ignorado quando EntityType = C
.
- Um pouco menos eficiente quando
E
é um tipo de comprimento fixo, e uma grande parte das linhas é para EntityType de C
(ou seja, não está sendo usado, E
portanto NULL
) e não está usando a SPARSE
opção na coluna ou Compactação de Dados no Índice Clusterizado
- Potencialmente menos eficiente para consultas que não precisam,
E
uma vez que a presença E
na tabela de tipo base aumentará o tamanho de cada linha, o que diminui o número de linhas que podem caber em uma página de dados. Mas isso é altamente dependente do tipo de dados exato de E
, FILLFACTOR, quantas linhas existem na tabela de tipo base etc.
Manter propriedade E
em cada tabela de subtipo
PROs
- Modelo de dados mais limpo (ou seja, não precisa se preocupar em educar os outros sobre o motivo pelo qual a coluna
E
na tabela de tipo base não deve ser usada porque "realmente não existe")
- Provavelmente se parece mais com o modelo de objeto
- Pode marcar a coluna como
NOT NULL
se esta fosse uma propriedade necessária da entidade
- Não há necessidade de extra
CHECK CONSTRAINT
na tabela de tipo base para garantir que E IS NULL
quando EntityType = C
(embora isso não seja um grande ganho)
CONs
- Requer JOIN para subtipo Tabela (s) para obter essa propriedade
- Potencialmente um pouco menos eficiente quando necessário
E
, devido a JOIN, dependendo de quantas linhas de A
+ B
você possui, em oposição a quantas linhas C
existem.
- Um pouco mais difícil / complexo para operações que lidam apenas com entidades
A
e B
(e não C
) como sendo do mesmo "tipo". Obviamente, você pode abstrair isso por meio de uma Visualização que faz um UNION ALL
entre uma SELECT
das tabelas JOINed de A
e outra SELECT
das tabelas JOINed de B
. Isso vai reduzir a complexidade de consultas SELECT, mas não tão útil para INSERT
e UPDATE
consultas.
- Dependendo das consultas específicas e da frequência com que são executadas, isso pode ser uma ineficiência potencial nos casos em que ter um índice
(D, E)
realmente ajudaria uma ou mais consultas usadas com frequência, pois elas não podem ser indexadas juntas.
Normalize E
para tabela intermediária entre classe base e A
&B
(Observe que eu gosto da resposta do @ MDCCL como uma alternativa viável, dependendo das circunstâncias. O que se segue não se destina a ser uma crítica estrita a essa abordagem, mas como um meio de adicionar alguma perspectiva - a minha, é claro - avaliando no mesmo contexto das duas opções que eu já havia proposto, o que tornará mais fácil esclarecer o que considero a diferença relativa entre normalização total e a abordagem atual da normalização parcial.)
PROs
- o modelo de dados é totalmente normalizado (não pode haver nada de errado com isso, pois é para isso que os RDBMSs são projetados)
- complexidade de consulta reduzida para consultas que precisam
A
e B
, mas não C
(ou seja, não há necessidade de duas consultas unidas via UNION ALL
)
CONs
- um pouco mais de espaço ocupado (a
Bar
tabela duplica o ID e há uma nova coluna BarTypeCode
) [insignificante, mas algo para estar ciente]
- ligeiro aumento na complexidade consulta como um adicional
JOIN
é necessário para obter a qualquer A
ouB
- aumento da área de superfície para bloqueio, principalmente ativada
INSERT
( DELETE
pode ser manipulada implicitamente através da marcação de Chaves estrangeiras como ON CASCADE DELETE
), pois a transação será mantida aberta um pouco mais na tabela da classe base (ie Foo
) [insignificante, mas algo para estar ciente]
nenhum conhecimento direto do tipo real - A
ou B
- na tabela da classe base Foo
; só conhece o tipo Br
que pode ser A
ou B
:
Ou seja, se você precisar fazer consultas sobre as informações gerais de base, mas precisar categorizar pelo tipo de entidade ou filtrar um ou mais tipos de entidade, a tabela da classe base não terá informações suficientes; nesse caso, você precisará LEFT JOIN
a Bar
mesa Isso também reduzirá a eficácia da indexação da FooTypeCode
coluna.
nenhuma abordagem consistente para interagir com o A
& B
vs C
:
Ou seja, se cada entidade se relacionar diretamente com a tabela da classe base, de modo que haja apenas um JOIN para obter a entidade completa, todos poderão criar uma familiaridade mais rápida e fácil em termos de trabalho com o modelo de dados. Haverá uma abordagem comum para consultas / procedimentos armazenados que os torna mais rápidos para desenvolver e menos propensos a ter bugs. Uma abordagem consistente também torna mais rápido e fácil adicionar novos subtipos no futuro.
potencialmente menos adaptável às regras de negócios que mudam com o tempo:
Ou seja, as coisas sempre mudam e é bastante fácil passar E
para a Tabela da classe base se isso se tornar comum a todos os subtipos. Também é fácil mover uma propriedade comum para os subtipos se as mudanças na natureza das entidades fizerem uma mudança que valha a pena. É fácil o suficiente dividir um subtipo em dois subtipos (basta criar outro SubTypeID
valor) ou combinar dois ou mais subtipos em um. Por outro lado, e se E
mais tarde se tornar uma propriedade comum de todos os subtipos? Então a camada intermediária da Bar
tabela não teria sentido e a complexidade adicionada não valeria a pena. Obviamente, é impossível saber se tal mudança aconteceria em 5 ou 10 anos, portanto a Bar
tabela não é necessariamente, nem mesmo com grande probabilidade de ser uma má ideia (foi por isso que disse " potencialmente menos adaptável"). Estes são apenas pontos a considerar; é uma aposta em qualquer direção.
agrupamento potencialmente inadequado:
Ou seja, apenas porque a E
propriedade é compartilhada entre tipos de entidade A
e B
não significa isso A
e B
deve ser agrupada. Só porque as coisas "parecem" as mesmas (ou seja, mesmas propriedades) não significa que são as mesmas.
Sumário
Assim como decidir se / quando desnormalizar, a melhor maneira de abordar essa situação específica depende de considerar os seguintes aspectos do uso do modelo de dados e garantir que os benefícios superem os custos:
- quantas linhas você terá para cada EntityType (procure pelo menos 5 anos no futuro, assumindo um crescimento acima da média)
- quantos GB cada uma dessas tabelas (tipo base e subtipos) terá em 5 anos?
- que tipo de dados específico é propriedade
E
- é apenas uma propriedade ou existem algumas ou mesmo várias propriedades
- de quais consultas você precisará
E
e com que frequência elas serão executadas
- de que consultas você precisará, que não precisam
E
e com que frequência elas serão executadas
Eu acho que tenho como padrão manter E
as tabelas de subtipo separadas porque é, no mínimo, "mais limpo". Eu consideraria mudar E
para a tabela do tipo base SE: a maioria das linhas não era para EntityType of C
; e o número de linhas era pelo menos na casa dos milhões; e eu, na maioria das vezes, as consultas executadas que precisavam E
e / ou as que se beneficiariam de um índice (D, E)
são executadas com muita frequência e / ou exigem recursos suficientes do sistema, de modo que ter o índice reduz a utilização geral de recursos ou, pelo menos, impede surtos no consumo de recursos que ultrapassam níveis aceitáveis ou duram o suficiente para causar bloqueio excessivo e / ou aumento de impasses.
ATUALIZAR
O OP comentou sobre esta resposta que:
Meus empregadores mudaram a lógica do negócio, removendo E completamente!
Esta mudança é particularmente importante porque é exatamente o que eu predicado pode acontecer no "contras" subseção da "Normalizar E
para Tabela intermediário entre base-classe e A
& B
" acima (6º ponto). A questão específica é quão fácil / difícil é refatorar o modelo de dados quando essas alterações ocorrem (e sempre acontecem). Alguns argumentam que qualquer modelo de dados pode ser refatorado / alterado, então comece com o ideal. Mas, embora seja verdade, em nível técnico, que qualquer coisa possa ser refatorada, a realidade da situação é uma questão de escala.
Os recursos não são infinitos, não apenas CPU / Disco / RAM, mas também recursos de desenvolvimento: tempo e dinheiro. As empresas estão constantemente definindo prioridades nos projetos porque esses recursos são muito limitados. E muitas vezes (pelo menos na minha experiência), projetos para obter eficiência (mesmo o desempenho do sistema, além de um desenvolvimento mais rápido / menos bugs) são priorizados abaixo dos projetos que aumentam a funcionalidade. Embora seja frustrante para nós, pessoal técnico, porque entendemos quais são os benefícios a longo prazo dos projetos de refatoração, é da natureza dos negócios que os profissionais menos técnicos têm mais facilidade em ver a relação direta entre novas funcionalidades e novas receita. O que isso se resume é: "voltaremos para corrigir isso mais tarde" == "
Com isso em mente, se o tamanho dos dados for pequeno o suficiente para que as alterações possam ser feitas muito e / ou você tenha uma janela de manutenção que seja longa o suficiente para não apenas fazer as alterações, mas também reverter se algo ocorrer errado, a normalização E
para uma Tabela intermediária entre a tabela da classe base e as tabelas A
& B
subclasse poderia funcionar (embora isso ainda não o deixe com conhecimento direto do tipo específico ( A
ouB
) na tabela da classe base). MAS, se você tiver centenas de milhões de linhas nessas tabelas e uma quantidade incrível de código referenciando as tabelas (código que precisa ser testado quando as alterações são feitas), geralmente vale a pena ser mais pragmático do que idealista. E esse é o ambiente com o qual tive que lidar por anos: 987 milhões de linhas e 615 GB na tabela de classe base, espalhados por 18 servidores. E tanto código atingiu essas tabelas (tabelas de classe base e subclasse) que houve muita resistência - principalmente do gerenciamento, mas às vezes do resto da equipe - a fazer alterações devido à quantidade de desenvolvimento e desenvolvimento. Recursos de controle de qualidade que precisariam ser alocados.
Portanto, mais uma vez, a abordagem "melhor" só pode ser determinada situação por situação: você precisa conhecer seu sistema (ou seja, quantos dados e como todas as tabelas e códigos se relacionam), como realizar a refatoração e as pessoas com quem você trabalha (sua equipe e possivelmente a gerência - você pode obter o apoio deles para esse projeto?). Há algumas mudanças que eu mencionei e planejei por 1 a 2 anos e precisei de vários sprints / lançamentos para obter talvez 85% deles implementados. Mas se você tiver apenas <1 milhão de linhas e não houver muito código associado a essas tabelas, provavelmente poderá começar do lado mais ideal / "puro".
Lembre-se de que, da maneira que você escolher, preste atenção em como esse modelo funciona nos próximos 2 anos, pelo menos (se possível). Preste atenção no que funcionou e no que causou dor, mesmo que parecesse a melhor ideia da época (o que significa que você também precisa se permitir aceitar estragar tudo - todos nós fazemos - para que você possa avaliar honestamente os pontos de dor ) E preste atenção ao motivo pelo qual certas decisões funcionaram ou não, para que você possa tomar decisões com maior probabilidade de serem "melhores" da próxima vez :-).
De acordo com minha interpretação de suas especificações, você deseja encontrar um método para implementar duas estruturas de supertipo-subtipo diferentes (mas conectadas ) .
Para expor uma abordagem para alcançar a tarefa mencionada, adicionarei ao cenário em questão os dois tipos clássicos de entidades hipotéticas denominados
Foo
eBar
, que detalharei a seguir.Regras do negócio
Aqui estão algumas instruções que me ajudarão a criar um modelo lógico:
A Foo is either one Bar or one C
A Foo is categorized by one FooType
A Bar is either one A or one C
A Bar is classified by one BarType
Modelo lógico
E então, o modelo lógico IDEF1X [1] resultante é mostrado na Figura 1 (e você pode baixá-lo do Dropbox como um PDF também):
A adição de Foo e Bar
Não adicionei
Foo
eBar
fiz o modelo parecer melhor, mas para torná-lo mais expressivo. Considero que são importantes devido ao seguinte:Como
A
eB
compartilha o atributo nomeadoE
, esse recurso sugere que eles são tipos de subentidade de um tipo distinto (mas relacionado) de conceito , evento , pessoa , medida etc., que eu representei por meio doBar
tipo de superentidade que, por sua vez, é um tipo de subentidade deFoo
, que mantém oD
atributo na parte superior da hierarquia.Como
C
apenas compartilha um atributo com o restante dos tipos de entidade em discussão, ou seja,D
esse aspecto insinua que é um tipo de subentidade de outro tipo de conceito , evento , pessoa , medida , etc., então eu descrevi essa circunstância em virtude de oFoo
tipo de super entidade.No entanto, essas são apenas suposições e, como um banco de dados relacional deve refletir com precisão a semântica de um determinado contexto de negócios , é necessário identificar e classificar todas as coisas de interesse em seu domínio específico, para que você possa captar com mais precisão .
Fatores importantes na fase de projeto
É bastante útil estar ciente do fato de que, deixando toda a terminologia de lado, um cluster exclusivo de supertipo-subtipo é um relacionamento comum. Vamos descrever a situação da seguinte maneira:
Assim, há uma correspondência (ou cardinalidade) de um para um (1: 1) nesses casos.
Como você sabe nas postagens anteriores, o atributo discriminador (coluna, quando implementado) desempenha um papel primordial ao criar uma associação dessa natureza, porque indica a instância correta do subtipo ao qual o supertipo está conectado . A migração da CHAVE PRIMÁRIA de (i) o supertipo para (ii) os subtipos também é de importância primordial.
Estrutura DDL em concreto
E então eu escrevi uma estrutura DDL baseada no modelo lógico apresentado acima:
Com essa estrutura, você evita o armazenamento de marcas NULL em suas tabelas (ou relações ) de base, o que introduziria ambiguidade em sua base de dados.
Integridade, consistência e outras considerações
Depois de implementar seu banco de dados, você deve garantir que (a) cada linha de supertipo exclusivo seja sempre complementada por sua contraparte de subtipo correspondente e, por sua vez, garanta que (b) essa linha de subtipo seja compatível com o valor contido na coluna discriminadora de supertipo . Portanto, é bastante conveniente empregar o ACID
TRANSACTIONS
para garantir que essas condições sejam atendidas no seu banco de dados.Você não deve abandonar a solidez lógica, a auto-expressividade e a precisão do seu banco de dados, esses são aspectos que decididamente tornam seu banco de dados mais sólido.
As duas respostas postadas anteriormente já incluem pontos pertinentes que certamente valem a pena levar em consideração ao projetar, criar e gerenciar seu banco de dados e seus programas aplicativos.
Recuperando dados por meio das definições de VIEW
Você pode configurar algumas visualizações que combinam colunas dos diferentes grupos de supertipos e subtipos , para recuperar os dados em mãos sem, por exemplo, escrever as cláusulas JOIN necessárias sempre. Dessa forma, você pode SELECIONAR diretamente DA VISTA (uma relação ou tabela derivada ) de interesse com facilidade.
Como você pode ver, "Ted" Codd foi, sem dúvida, um gênio. As ferramentas que ele legou são bastante fortes e elegantes e, é claro, estão bem integradas entre si.
Recursos relacionados
Se você deseja analisar algum banco de dados extenso que envolva relacionamentos supertipo-subtipo, você achará valiosas as respostas extraordinárias propostas por @PerformanceDBA para as seguintes perguntas sobre estouro de pilha:
Banco de dados histórico / auditável .
Ou uma tabela ou muitas para muitos eventos diferentes, mas que interagem? , que compreende subtipos aninhados .
Nota
1. A Definição de Integração para Modelagem de Informações ( IDEF1X ) é uma técnica de modelagem de dados altamente recomendável que foi estabelecida como padrão em dezembro de 1993 pelo Instituto Nacional de Padrões e Tecnologia ( NIST ) dos Estados Unidos . É solidamente baseado em (a) o material teórico inicial criado pelo Dr. EF Codd; em (b) a entidade-relacionamento vista de dados, desenvolvido por Dr. PP Chen ; e também (c) a Logical Database Design Technique, criada por Robert G. Brown. Vale ressaltar que o IDEF1X foi formalizado por meio de lógica de primeira ordem.
fonte
E
completamente! A razão para aceitar a resposta do usuário srutzky é porque ela fornece bons pontos que me ajudam a tomar minha decisão de escolher a rota mais eficiente. Se não fosse por isso, eu aceitaria sua resposta. Eu votei sua resposta mais cedo. Obrigado novamente!