Normalização: é considerado compatível dividir valores estáticos e numéricos como um ano em sua própria tabela?

16

Estou tendo uma discussão interessante com outro designer de banco de dados sobre normalização. Neste exemplo, temos uma tabela GameTitles e cada registro deve conter o ano em que o jogo foi lançado. Ele diz que a 2NF exige que tudo deva ser normalizado; portanto, para estar em conformidade, o campo ano deve ser dividido em uma tabela ReleaseYears com sua própria chave primária, referenciada pela tabela GameTitles. Eu digo que deve permanecer como um campo na própria tabela GameTitles.

Meu argumento para isso é que um ano é apenas um valor numérico não primitivo que é estático por sua própria natureza (ou seja, 2011 sempre será 2011). Devido a isso, ele serve como seu próprio identificador e não precisa de nada para fazer referência a ele, pois é o que é. Isso também introduz manutenção adicional, pois agora você precisa adicionar um novo ano à tabela apenas para fazer referência a ela. Se você preencher previamente a tabela com um grande intervalo de anos, terá registros extras que potencialmente não terão referências a eles. Isso também aumenta o tamanho do banco de dados, pois agora você possui uma tabela extra, sobrecarga de registro e a chave primária adicional para o ano em si. Se você mantiver o ano como um campo na tabela GameTitles, você eliminará toda essa manutenção adicional e despesas gerais.

Pensamentos sobre isso?

edit: destinado a postar isso no StackOverflow. Alguém pode votar para excluir ou sinalizar isso para obter atenção?

stoogemuffin
fonte
6
Por quê então? parece um bom ajuste aqui.
precisa
A pergunta que quero fazer é: você está perguntando sobre normalização ou necessidades reais de produção? Para produção, perguntaria se isso é uma coisa válida a se fazer.
jcolebrand

Respostas:

14

O outro designer de banco de dados está simplesmente errado, mas seu raciocínio também está errado. Suponha que você comece com esta tabela, que possui uma única chave candidata, "game_title".

Table: game_titles

game_title                      year_first_released
--
The first game                  1998
The second game                 1999
Best game: the third one        2001
The fourth game                 2003
Forty-two, the end of games     2011

Você avalia se está na 2NF fazendo a si mesmo essas perguntas.

P: Primeiro de tudo, é 1NF?

A: Sim é.

P: Quais são os atributos principais (atributos que fazem parte de uma chave candidata)?

R: "game_title" é o único atributo principal.

P: Quais são os atributos não principais?

R: "year_first_released" é o único.

P: O "year_first_released" depende funcionalmente de todo o "game_title", ou apenas de uma parte dele?

R: A única chave candidata, "game_title", é uma única coluna; nem tem partes. Portanto, "year_first_released" depende funcionalmente de todo o "game_title".

Voilà. Você encontrou 2NF.

Você pode cortar alguns dos termos formais perguntando primeiro se está em 1NF e respondendo a esta pergunta.

P: Existem chaves candidatas compostas?

A: Não.

Voilà. Você encontrou 2NF novamente.

Por definição, para uma tabela violar 2NF, ela precisa ter pelo menos uma chave candidata que tenha mais de uma coluna.

Aqui estão suas razões para rejeitar a opinião de seu amigo.

  • Um ano é apenas um valor numérico não primitivo.
  • Um ano é estático por sua própria natureza.
  • Um ano serve como seu próprio identificador.
  • Uma tabela de anos introduz manutenção adicional.
  • Uma tabela de anos pode ter linhas extras que não são referenciadas.
  • Uma tabela de anos aumenta o tamanho do banco de dados.

Nenhuma dessas razões tem nada a ver com a existência de uma tabela no 2NF.

Ao projetar um banco de dados, não é errado considerar problemas de manutenção, tamanho do banco de dados, linhas não referenciadas, restrições de intervalo e assim por diante. É errado chamar essas coisas de normalização.

Ah, e essa tabela de duas colunas que forneci acima - está na 5NF.

Mike Sherrill 'Recolha de Gatos'
fonte
2
Bem feito. Fiquei tentado a postar uma resposta que não dizia nada além da sua primeira frase ... "O outro designer de banco de dados está simplesmente errado", você abordou o porquê muito bem.
Mark-Storey-Smith
5

Criar uma tabela separada para qualquer atributo não tem nada a ver com normalização. 2NF, 3NF, BCNF, 4NF, 5NF estão todos preocupados com a eliminação de dependências não-chave. Se você remover qualquer atributo único de uma nova tabela e substituí-lo por um atributo de chave estrangeira, as dependências na tabela serão logicamente iguais às de antes - portanto, a versão revisada da tabela não será mais ou menos normalizada do que ela foi antes.

nvogel
fonte
Quero acrescentar algo a isso, mas não sei o quê. Você está dizendo que mover algo para uma tabela que tem uma correlação 1: 1 (ou 1 chave para exatamente 1 valor, como neste caso, ou uma linha para uma linha) não oferece benefício se a pesquisa não for necessária, certo? Mas há um benefício potencial de pesquisa se você raramente precisar do ano e estiver apenas com um intervalo de 255 anos ou menos. Você pode se dar bem com alguns bytes salvos aqui, mas como normalmente eles são alocados em 4 bytes de qualquer maneira, essa não é uma suposição razoável.
jcolebrand
11
@jcolebrand: Concorde com o que você diz. Ainda a resposta para a pergunta é a mesma: se você faz ou não, isso não tem nada a ver com normalização em si.
Nvogel
Eu concordo. Como eu disse, o meu meio que foi meio que "eu sinto que o OP está faltando alguma coisa aqui" ... porque não tenho certeza para onde ir com esse conceito.
jcolebrand
5

Do meu ponto de vista, uma tabela de anos separada só faria sentido se o "ano de lançamento" não for um ano civil, mas, por exemplo, um ano fiscal que pode abranger vários anos civis (por exemplo, de outubro a outubro).

Essa tabela conteria a definição (data real de início e término) do ano fiscal

um cavalo sem nome
fonte
11
+1 você só precisa de uma tabela se ele vai ter atributos :)
Jack Douglas
2

Em http://en.wikipedia.org/wiki/Second_normal_form :

uma tabela 1NF está em 2NF se, e somente se, dada qualquer chave candidata K e qualquer atributo A que não seja um constituinte de uma chave candidata, A depende de K inteiro, e não apenas de uma parte dele.

Você não indicou se o ano faz parte da chave do candidato ou não, mas não tenho certeza se isso importa, porque em ambos os casos o 2NF ficaria satisfeito no que diz respeito ao ano.

Em um nível prático, é uma má idéia separar o ano por todos os motivos listados.

Leigh Riffel
fonte
2

Não gosto do argumento contra a tabela separada por causa de seu tamanho ou por ter linhas não utilizadas. Mesmo se você colocar 1000 anos nessa tabela, o tamanho será insignificante.

Dito isto, não acho que a mesa seja necessária. Qual o sentido de ter uma mesa separada para o ano? Esses dados já estão na tabela principal e você não salva absolutamente nada criando uma segunda tabela.

O argumento pode ser diferente para uma tabela de calendário, em que cada linha representa um dia e pode ter outros atributos (dia da semana, deslocamento do UTC, se é feriado etc.).

Mas ano sozinho? Não, não vejo nenhum benefício ... E, como outros já apontaram, pergunte-lhes por que acham que isso é mais normalizado? Ou o que eles ganham? Se você estiver tentando escrever consultas como

WHERE othertable.year = 2011

Ao invés de

WHERE dt >= 20110101 AND dt < 20120101

Então eu tentaria convencê-lo de que o último é muito melhor para desempenho (assumindo que o dt está indexado) e armazenamento. Se a simplicidade da codificação for primordial, eu diria que uma coluna computada persistente seria melhor que outra tabela.

Aaron Bertrand
fonte
1

Eu concordo totalmente com a resposta de Catcall, exceto em um ponto: "ano" pode não ser sempre um valor primitivo, mas acho que esse é mais um conceito de lógica de negócios do que um conceito de design de banco de dados.

Mantendo o mesmo design, vamos supor que os anos devam ser apenas aqueles que são permitidos para liberação. Dessa forma, você não está lidando com valores numéricos primitivos, mas com um subconjunto deles, e como esse subconjunto não possui uma implementação primitiva, você deve fazer o seu próprio (uma tabela separada?) E referenciá-lo (com um FK). Dessa forma, ainda estamos falando de anos, mas precisamos gerenciá-los de uma maneira diferente, porque eles mudaram conceitualmente seu significado. No entanto, eles ainda são "ano de lançamento", mas conceitualmente diferentes em termos do que eles significam para alguém no conhecimento do domínio.

Para este caso específico, digo novamente que a resposta de Catcall está correta, mas só queria salientar isso. (Desculpe, não tenha representante suficiente para comentar ainda.)

Alfa
fonte