Estou abordando um projeto em que terei que implementar um banco de dados com meu chefe; somos uma startup muito pequena, portanto o ambiente de trabalho é profundamente pessoal.
Ele havia me dado um dos bancos de dados da empresa antes e isso foi totalmente contra o que eu aprendi (e li sobre) na escola para RDBMS. Por exemplo, existem bancos de dados inteiros aqui que consistem em uma tabela (por banco de dados independente). Uma dessas tabelas tem mais de 20 colunas e, para o contexto, aqui estão alguns dos nomes de colunas de uma tabela:
lngStoreID | vrStoreName | lngCompanyID | vrCompanyName | lngProductID | vrProductName
O ponto é que, onde ele deve ter tabelas individuais que contêm os dados da entidade (nome, tamanho, data da compra, etc.), ele coloca tudo em uma tabela grande por banco de dados.
Desejo melhorar esse design, mas não sei por que um modelo de dados segmentado e normalizado adequadamente realmente melhoraria esse produto. Embora eu esteja familiarizado com o design de banco de dados da faculdade e entenda como fazê-lo, não sei por que isso realmente melhora os bancos de dados.
Por que um bom esquema relacional melhora um banco de dados?
fonte
He [the boss] had given me one of his databases before and it completely went against what I was taught (and read about) in school for RDBMS
<- Bem-vindo ao mundo real!Respostas:
O argumento de desempenho é geralmente o mais intuitivo. Você deseja destacar especialmente como será difícil adicionar bons índices em um banco de dados normalizado incorretamente (nota: há casos extremos em que a desnormalização pode de fato melhorar o desempenho, mas quando você não tem experiência com bancos de dados relacionais, provavelmente não conseguirá facilmente veja esses casos).
Outro é o argumento do tamanho do armazenamento. Uma tabela desnormalizada com muitas redundâncias exigirá muito mais armazenamento. Isso também afeta o aspecto do desempenho: quanto mais dados você tiver, mais lentas serão as suas consultas.
Há também um argumento que é um pouco mais difícil de entender, mas é de fato mais importante porque você não pode resolvê-lo jogando mais hardware nele. Esse é o problema da consistência dos dados. Um banco de dados normalizado adequadamente cuidará por si só de que um produto com um ID específico sempre tenha o mesmo nome. Porém, em um banco de dados desnormalizado, essas inconsistências são possíveis; portanto, é necessário um cuidado especial para evitar inconsistências, o que levará tempo de programação para corrigir e ainda causará bugs que custarão a satisfação do cliente.
fonte
Usar o software dedicado de Gerenciamento de Banco de Dados pode ser consideravelmente mais fácil (desculpe; não pude resistir).
Se esse banco de dados se importar apenas com o "registro" de qual produto foi vendido, onde, quando e por quem, você poderá estender a definição de "banco de dados OK" o suficiente para cobri-lo. Se esses dados estiverem sendo usados para qualquer outra coisa , serão realmente muito ruins.
Mas ...
O aplicativo / consultas usando esses dados responde mal / lentamente? Caso contrário, não há problema real a ser resolvido. Claro, parece feio, mas se funcionar , você não receberá "pontos" por sugerir que "poderia" ser melhor.
Se você encontrar sintomas definidos (ou seja, problemas) que parecem causados por uma modelagem de dados deficiente, protótipo de uma solução melhor. Faça uma cópia de um desses "bancos de dados", normalize os dados e veja se sua solução funciona melhor. Se for consideravelmente melhor (e eu esperaria plenamente que qualquer operação de atualização nesses dados fosse massivamente aprimorada), volte para o seu chefe e mostre a eles a melhoria.
É perfeitamente possível recriar sua "exibição de tabela única" dos dados com ... bem ... visualizações.
fonte
A resposta é: nem sempre melhora um banco de dados. Você deve estar ciente de que o que você provavelmente aprendeu é chamado Terceira Forma Normal .
Outros formulários são válidos em algumas situações, o que é essencial para responder à sua pergunta. Seu exemplo se parece com o Primeiro formulário normal , se isso ajudar você a se sentir melhor sobre o estado atual.
As regras 3NF estabelecem relações entre dados que "melhoram" um banco de dados:
Impedir a entrada de dados inválidos no sistema (se um relacionamento for 1 para 1, isso força um erro, apesar do código escrito em cima dele). Se seus dados forem consistentes no banco de dados, é menos provável que resulte em inconsistências fora do banco de dados.
Ele fornece uma maneira de validar o código (por exemplo, um relacionamento muitos-para-um é um sinal para restringir as propriedades / comportamentos de um objeto). Ao escrever código para usar o banco de dados, às vezes os programadores percebem a estrutura de dados como um indicador de como o código deve funcionar. Ou eles podem fornecer feedback útil se o banco de dados não corresponder ao código. (Isso é mais como uma ilusão, infelizmente.)
Forneça regras que possam ajudar significativamente a reduzir erros ao criar um banco de dados, para que você não o construa com base em requisitos arbitrários que possam surgir a qualquer momento durante a vida de um banco de dados. Em vez disso, você está avaliando sistematicamente as informações para atingir objetivos específicos.
Estruturas de banco de dados adequadas levam a um desempenho aprimorado, conectando os dados de maneira a minimizar o armazenamento de dados, minimizar as chamadas de armazenamento para recuperar dados, maximizar os recursos na memória e / ou minimizar a classificação / manipulação de dados para o conjunto de dados específico que você possui, em comparação com a consulta que você está executando contra ele. Mas a estrutura "adequada" depende da quantidade de dados, natureza dos dados, tipo de consulta, recursos do sistema etc. Ao normalizar, você pode piorar o desempenho (ou seja, se você carregar todos os dados como uma tabela), a junção pode desacelerar uma consulta). O processamento de transações (OLTP) vs business intelligence (data warehouse) são muito diferentes.
Em uma empresa pequena com conjuntos de dados pequenos, você pode descobrir que não há nada errado com o que está ocorrendo agora. Exceto que, se você crescer, será difícil "consertar" mais tarde, porque, à medida que a tabela aumenta, os sistemas que a utilizam provavelmente serão mais lentos.
Geralmente, você deseja enfatizar transações rápidas à medida que a empresa cresce. No entanto, se você dedicar tempo a esse projeto agora, em vez de outras coisas que a empresa possa precisar com mais urgência, talvez nunca tenha esse problema, porque sua empresa nunca realmente cresce. Esse é o "desafio da pré-otimização" - onde gastar seu precioso tempo agora.
Boa sorte!
fonte
WHERE
cláusula Obviamente, eles ainda podem dar errado, mas é menos provável em uma situação normalizada, pois você só precisa corresponder uma linha via chave primária.Existem várias razões pelas quais o uso de uma grande "mesa divina" é ruim. Vou tentar ilustrar os problemas com um banco de dados de exemplo inventado. Vamos supor que você esteja tentando modelar eventos esportivos. Diremos que você deseja modelar jogos e as equipes que jogam nesses jogos. Um design com várias tabelas pode se parecer com isso (isso é muito simplista de propósito, portanto, não seja pego em locais onde mais normalização possa ser aplicada):
e um banco de dados de tabela única ficaria assim
Primeiro, vejamos como criar índices nessas tabelas. Se eu precisasse de um índice na cidade natal para uma equipe, poderia adicioná-lo à
Teams
mesa ou àTeamsAndGames
mesa com bastante facilidade. Lembre-se de que sempre que você cria um índice, ele precisa ser armazenado no disco em algum lugar e atualizado à medida que as linhas são adicionadas à tabela. No caso daTeams
tabela, isso é bastante direto. Coloquei uma nova equipe, o banco de dados atualiza o índice. Mas e paraTeamsAndGames
quê? Bem, o mesmo se aplica a partir doTeams
exemplo. Eu adiciono uma equipe, o índice é atualizado. Mas também acontece quando adiciono um jogo! Mesmo que esse campo seja nulo para um jogo, o índice ainda precisa ser atualizado e armazenado em disco para esse jogo. Para um índice, isso não parece muito ruim. Mas quando você precisa de muitos índices para as várias entidades amontoadas nesta tabela, você perde muito espaço armazenando os índices e muito tempo do processador atualizando-os para itens onde eles não se aplicam.Segundo, consistência dos dados. No caso de usar duas mesas separadas, posso usar chaves estrangeiras
Games
daTeams
mesa para definir quais times estão jogando em um jogo. E, assumindo que as colunasHomeTeamId
eAwayTeamId
não são anuláveis, o banco de dados garantirá que todos os jogos que eu participe tenham 2 equipes e que essas equipes existam no meu banco de dados. Mas e o cenário de tabela única? Bem, como existem várias entidades nesta tabela, essas colunas devem ser anuláveis (você pode torná-las não anuláveis e colocar dados de lixo nela, mas isso é apenas uma ideia horrível). Se essas colunas forem anuláveis, o banco de dados não poderá mais garantir que, quando você insere um jogo, ele possui duas equipes.Mas e se você decidir fazer isso de qualquer maneira? Você configura as chaves estrangeiras de modo que esses campos aponte para outra entidade na mesma tabela. Mas agora o banco de dados apenas garantirá que essas entidades existam na tabela, não que sejam do tipo correto. Você pode facilmente definir
GameHomeTeamId
o ID de outro jogo e o banco de dados não irá reclamar. Se você tentasse isso no cenário de várias tabelas, o banco de dados seria adequado.Você pode tentar mitigar esses problemas dizendo "bem, apenas garantiremos que nunca façamos isso no código". Se você está confiante em sua capacidade de escrever código sem erros pela primeira vez e em levar em conta todas as combinações estranhas de coisas que um usuário pode tentar, vá em frente. Pessoalmente, não estou confiante em minha capacidade de fazer uma dessas coisas, por isso deixarei o banco de dados fornecer uma rede de segurança extra.
(Isso fica ainda pior se seu design é aquele em que você copia todos os dados relevantes entre linhas em vez de usar chaves estrangeiras. Qualquer inconsistência ortográfica / de outros dados será difícil de resolver. Como você pode saber se "Jon" é um erro de ortografia de "John "ou se foi intencional (porque são duas pessoas separadas)?)
Terceiro, quase todas as colunas precisam ser anuláveis ou devem ser preenchidas com dados copiados ou com lixo. Um jogo não precisa de um
TeamName
ouTeamHomeCity
. Portanto, todo jogo precisa de algum tipo de espaço reservado ou precisa ser anulável. E se for anulável, o banco de dados terá prazer em jogar semTeamName
. Também será necessária uma equipe sem nome, mesmo que sua lógica de negócios diga que isso nunca deve acontecer.Existem várias outras razões pelas quais você deseja tabelas separadas (incluindo a preservação da sanidade do desenvolvedor). Existem até algumas razões pelas quais uma tabela maior pode ser melhor (a desnormalização às vezes melhora o desempenho). Esses cenários são poucos e distantes entre si (e geralmente são mais bem tratados quando você tem métricas de desempenho para mostrar que esse é realmente o problema, não um índice ausente ou algo mais).
Por fim, desenvolva algo que seja fácil de manter. Só porque "funciona" não significa que está tudo bem. Tentar manter tabelas de deus (como classes de deus) é um pesadelo. Você está se preparando para a dor mais tarde.
fonte
Citação do dia: " Teoria e prática devem ser as mesmas ... em teoria "
Tabela não normalizada
Sua tabela exclusiva, que contém todos os dados redundantes, tem uma vantagem: torna o relatório em suas linhas muito simples de codificar e rápido de executar, porque você não precisa fazer nenhuma junção. Mas isso a um alto custo:
IngCompanyID
evrCompanyName
). A atualização dos dados mestre pode exigir a atualização de muito mais linhas do que em um esquema normalizado.Tabela normalizada
As desvantagens acima são vantagens para o esquema normalizado. Obviamente, as consultas podem ser um pouco mais complexas para escrever.
Em resumo, o esquema normalizado expressa muito melhor a estrutura e as relações entre seus dados. Vou ser provocativo e dizer que é o mesmo tipo de diferença que entre a disciplina necessária para usar um conjunto de gavetas ordenadas do escritório e a facilidade de uso de uma lixeira.
fonte
Eu acho que há pelo menos duas partes em sua pergunta:
1. Por que entidades de tipos diferentes não devem ser armazenadas na mesma tabela?
As respostas mais importantes aqui são legibilidade e velocidade do código. A
SELECT name FROM companies WHERE id = ?
é muito mais legível que aeSELECT companyName FROM masterTable WHERE companyId = ?
é menos provável que você faça perguntas acudidas sem sentido (por exemploSELECT companyName FROM masterTable WHERE employeeId = ?
, não seria possível quando empresas e funcionários estiverem armazenados em tabelas diferentes). Quanto à velocidade, os dados de uma tabela de banco de dados são recuperados lendo a tabela completa sequencialmente ou lendo um índice. Ambos são mais rápidos se a tabela / índice contiver menos dados, e é o caso se os dados forem armazenados em tabelas diferentes (e você só precisará ler uma das tabelas / índices).2. Por que as entidades de um único tipo devem ser divididas em subentidades armazenadas em tabelas diferentes?
Aqui, o motivo é principalmente para evitar inconsistências de dados. Com a abordagem de tabela única, para um sistema de gerenciamento de pedidos, você pode armazenar o nome do cliente, o endereço do cliente e o ID do produto que o cliente solicitou como uma única entidade. Se um cliente encomendasse vários produtos, você teria várias instâncias do nome e endereço do cliente em seu banco de dados. Na melhor das hipóteses, você acabou de obter dados duplicados no seu banco de dados, o que pode desacelerar um pouco. Mas o pior é que alguém (ou algum código) cometeu um erro quando os dados foram inseridos, para que as empresas terminem com endereços diferentes no seu banco de dados. Só isso já é ruim o suficiente. Mas se você consultar o endereço de uma empresa com base no nome (por exemplo,
SELECT companyAddress FROM orders WHERE companyName = ? LIMIT 1
) você apenas arbitrariamente obteria um dos dois endereços retornados e nem perceberia que havia uma inconsistência. Porém, toda vez que você executa a consulta, é possível obter um endereço diferente, dependendo de como sua consulta é resolvida internamente pelo DBMS. Isso provavelmente quebrará seu aplicativo em outro lugar, e a causa raiz dessa quebra será muito difícil de encontrar.Com a abordagem de várias tabelas, você perceberia que existe uma dependência funcional do nome da empresa para o endereço da empresa (se uma empresa puder ter apenas um endereço), você armazenaria a tupla (companyName, companyAddress) em uma tabela (por exemplo,
company
) e a tupla (productId, companyName) em outra tabela (por exemploorder
). UmaUNIQUE
restrição nacompany
tabela poderia impor que cada empresa tivesse apenas um único endereço no banco de dados, para que nenhuma inconsistência nos endereços da empresa pudesse surgir.Nota: na prática, por motivos de desempenho, você provavelmente geraria um companyId exclusivo para cada empresa e o usaria como uma chave estrangeira em vez de usar diretamente o companyName. Mas a abordagem geral permanece a mesma.
fonte
TL; DR - Eles estão projetando o banco de dados com base em como eles foram ensinados quando estavam na escola.
Eu poderia ter escrito essa pergunta há 10 anos. Levei algum tempo para entender por que meus antecessores projetaram seus bancos de dados da maneira que eles fizeram. Você está trabalhando com alguém que:
Não suspeito que seja o número 1, pois você realmente tem números de identificação em sua tabela, portanto, assumirei o número 2.
Depois que saí da escola, estava trabalhando para uma loja que usava um AS / 400 (também conhecido como IBM i). Encontrei algumas coisas estranhas na maneira como eles projetaram seus bancos de dados e comecei a advogar que fizéssemos alterações para seguir como me ensinaram a projetar bancos de dados. (Eu era burro naquela época)
Foi preciso um programador mais velho e paciente para me explicar por que as coisas foram feitas dessa maneira. Eles não haviam mudado o esquema porque isso causaria a quebra de programas mais antigos do que eu. Literalmente, o código fonte de um programa tinha uma data de criação do ano anterior ao meu nascimento. No sistema em que estávamos trabalhando, os programas deles precisavam implementar toda a lógica e operações que o planejador de consultas do banco de dados gerencia para você. (Você pode ver isso executando EXPLAIN em uma de suas consultas)
Ele estava atualizado sobre as técnicas que eu estava tentando implementar, mas manter o sistema em execução era mais importante do que fazer alterações "porque foi contra o que me ensinaram". Todo novo projeto que qualquer um de nós começava fazia o melhor uso possível do modelo relacional. Infelizmente, outros programadores / consultores da época ainda projetavam seus bancos de dados como se estivessem trabalhando com as restrições anteriores desse sistema.
Alguns exemplos do que encontrei que não se encaixavam no modelo relacional:
code1,code2, ..., code20
)As razões que me foram dadas para essas decisões de design foram baseadas nas restrições do sistema quando o banco de dados foi projetado pela primeira vez.
Datas - Disseram-me que demorava mais tempo de processamento para usar as funções de data (que mês ou dia ou dia da semana) para processar uma data do que criar uma tabela de todas as datas possíveis com todas essas informações.
Colunas sequenciais do mesmo tipo - O ambiente de programação em que estavam permitia que um programa criasse uma variável de matriz ao longo de parte da linha. E era uma maneira mais fácil de reduzir o número de operações de leitura.
Colunas CHAR de comprimento NxM - Era mais fácil inserir valores de configuração em uma coluna para reduzir as operações de leitura de arquivos.
Um exemplo mal concebido em C equivalente para refletir o ambiente de programação que eles tinham:
De acordo com o que me disseram, parte disso foi considerada uma prática recomendada na época.
fonte