Um novo requisito surgiu em uma antiga base de código, que basicamente permite a comunicação direta (interna) entre duas classes de usuários anteriormente não diretamente relacionadas (armazenadas em tabelas diferentes com esquema completamente diferente e, infelizmente, o código não reconhece o OO, muito menos projetada, para que não haja classe pai). Como estamos ansiosos por essa configuração antiga que nunca considerou essa funcionalidade, não há garantia de que não haja colisões de PK - dado o conjunto de dados em uso, é praticamente garantido que EXISTE.
Portanto, a solução parece óbvia: mate-a com fogo e reescreva toda a bagunça Uma tabela de mapeamento. Eu recebi duas direções para as possíveis maneiras de implementar o mapa, mas não sou um DBA, por isso não tenho certeza se existem prós e contras que perdi.
Para esclarecer a abstração, considere três grupos de dados de usuários diferentes: Professores, Administração, Estudantes (Não, isso não é tarefa de casa. Promessa!)
Mapeamento 1
(professor_id, admin_id e student_id são chaves estrangeiras para suas respectivas tabelas)
| mailing_id (KEY) | professor_id | admin_id | student_id |
-------------------------------------------------------
| 1001 | NULL | 87 | NULL |
| 1002 | 123 | NULL | NULL |
| 1003 | NULL | NULL | 123 |
O +/- dessa abordagem parece bastante pesado sobre os contras:
- Dois campos "desperdiçados" por linha
- Viola 2NF
- Vulnerável para inserir / atualizar anomalias (uma linha com apenas 0-1 conjunto de campos NULL, por exemplo)
Os profissionais não têm seus próprios méritos, no entanto:
- O mapeamento pode ser realizado com uma única pesquisa
- Determine facilmente os dados de "origem" de um determinado usuário no mailing_id
Verdade seja dita, no meu intestino, eu não gosto dessa idéia.
Mapeamento 2
(suponha que MSG_ * sejam constantes definidas, tipos de enumeração ou outro identificador adequado)
| mailing_id (KEY) | user_type (UNIQUE1) | internal_id (UNIQUE2)|
------------------------------------------------------------------
| 1001 | MSG_ADMIN | 87 |
| 1002 | MSG_PROF | 123 |
| 1003 | MSG_STUDENT | 123 |
Com essa configuração, e um índice composto exclusivo de {user_type, internal_id} as coisas ficam muito mais limpas, o 3NF é mantido e o código do aplicativo não precisa verificar anomalias de I / U.
Por outro lado, há um pouco de perda de transparência na determinação das tabelas de origem do usuário que precisam ser tratadas fora do banco de dados, equivalendo basicamente a um mapeamento no nível de aplicativo dos valores de user_type para as tabelas. No momento, estou (com bastante força) inclinando-me para esse segundo mapeamento, pois a desvantagem é um pouco menor.
MAS estou dolorosamente ciente de minhas próprias limitações e tenho certeza de que provavelmente perdi vantagens ou obstáculos em ambas as direções, por isso volto a mentes mais sábias do que as minhas.
fonte
Respostas:
Sua segunda ideia é a correta. Essa abordagem permite fazer todo o mapeamento necessário para integrar seus três espaços-chave em colisão.
É importante ressaltar que ele permite que o banco de dados imponha a maior parte da consistência necessária, usando restrições declarativas .
Você já tem mais código do que deseja, portanto, não adicione mais do que o absolutamente necessário para manter sua lista de chaves integrada consistente. Deixe seu mecanismo de banco de dados fazer o que foi criado para fazer.
O "filho problemático" que está causando desconforto no Mapeamento 2 é a
USER_TYPE
coluna. Esta coluna é importante porque você precisa garantir queINTERNAL_ID
apareça apenas uma vez por tipo de usuário. A única vez em que você precisa de qualquer código que tenha conhecimentoUSER_TYPE
é o código que insere e exclui da sua tabela de mapeamento. Isso pode ser localizado muito bem. Suponho que você criará um único ponto no seu código em que o conteúdo da tabela de mapeamento é mantido. Uma coluna extra neste local onde os dados são gravados não é grande coisa. O que você realmente deseja evitar é adicionar a coluna extra em todos os lugares em que os dados são lidos .O código nos seus sub-aplicativos que precisa usar o mapeamento pode ser totalmente ignorante,
USER_TYPE
simplesmente fornecendo a cada sub-aplicativo uma visualização que filtra os mapeamentos até o tipo de usuário específico do aplicativo.fonte
Por experiência, minha recomendação é escolher consistência em vez de elegância ou "melhores práticas". Isso é para corresponder ao design existente e combinar com TRÊS tabelas de correspondência (uma para cada função) com uma
mailing_id, user_id
estrutura de campo simples .É deselegante, mas tem algumas vantagens ...
Tenho certeza que muitos outros discordarão dessa abordagem, mas os principais objetivos da normalização e das práticas recomendadas são tornar o código mais consistente, para que seja mais fácil seguir e depurar ... e obviamente trazer toda a base de código para o zero provavelmente não é viável.
fonte