Quando você começa a "campos definidos pelo usuário", como é freqüentemente encontrado em rastreadores de bugs, gerenciamento de recursos do cliente e ferramentas de negócios similares, é que eles não recebem backup de uma tabela com vários bilhões de campos (se houver, provavelmente é um problema de próprio).
Em vez disso, o que você encontra são os designs da tabela Valor do atributo da entidade e a ferramenta de administração associada para gerenciar os atributos válidos.
Considere a seguinte tabela:
+ -------------- +
| coisa |
| -------------- |
| id |
| tipo |
| desc |
| attr1 |
| attr2 |
| attr3 |
| attr4 |
| attr5 |
+ -------------- +
Isso ocorre após você adicionar alguns atributos. Em vez de attr1
fingir que lê artist
ou tracks
ou genre
ou quaisquer atributos que a coisa tenha. E em vez de 5, e se fosse 50. Claramente isso é incontrolável. Também requer uma atualização do modelo e reimplementação do aplicativo para manipular um novo campo. Não é ideal.
Agora considere a seguinte estrutura de tabela:
+ -------------- + + --------------- + + ------------- +
| coisa | | thing_attr | | attr |
| -------------- | | --------------- | | ------------- |
| id | <--- + | coisa_id (fk) | +> | id |
| tipo | | attr_id (fk) | + - + | nome |
| desc | | valor | | |
+ -------------- + + --------------- + + ------------- +
Você tem a sua coisa com seus campos básicos. Você tem mais duas mesas. Um com os atributos. Cada campo é uma linha na attr
tabela. E depois há o thing_attr
com um par de chaves estrangeiras relacionadas à thing
mesa e à attr
mesa. E isso então tem um campo de valor onde você armazena qualquer que seja o valor do campo para essa entidade.
E agora você tem uma estrutura em que a tabela attr pode ser atualizada em tempo de execução e novos campos podem ser adicionados (ou removidos) rapidamente, sem impacto significativo no aplicativo em geral.
As consultas são um pouco mais complexas e a validação também se torna mais complexa (procedimentos armazenados descolados ou todo o lado do cliente). É uma troca de design.
Considere também a situação em que algum dia você precisará fazer uma migração e voltar ao aplicativo para descobrir que agora existem meia dúzia de atributos a mais do que o esquema que você distribuiu originalmente. Isso cria migrações e atualizações feias, em que a tabela Valor do atributo da entidade, quando usada corretamente, pode ser mais limpa. (Nem sempre, mas pode ser.)
Existem desvantagens em apenas modificar o esquema em tempo de execução? Se o usuário acha que algo precisa de um novo atributo, adicione dinamicamente uma coluna à tabela?
Se você estiver trabalhando com o sabor apropriado do banco de dados nosql, provavelmente poderá fazer isso (observe que o sabor apropriado do nosql para isso provavelmente seria um armazenamento de valor-chave que é, assim, a tabela EAV para os relacionais descritos acima) sem muita dificuldade. No entanto, ele vem com todos os compromissos do nosql, que são descritos em outros lugares em grandes detalhes.
Se você estiver trabalhando em um banco de dados relacional - precisará do esquema. Adicionar a coluna dinamicamente significa que algum subconjunto das seguintes coisas é verdadeiro:
- Você está fazendo programação de meta-banco de dados. Em vez de ser capaz de mapear corretamente essa coluna para esse campo com um ORM legal, você provavelmente está fazendo coisas como
select *
e, em seguida, fazendo um código complexo para descobrir quais são realmente os dados (consulte ResultSetMetaData do Java ) e armazenando-os em um mapa ( ou algum outro tipo de dados - mas não campos agradáveis no código). Isso então descarta um pouco de segurança de digitação e tipo que você possui com a abordagem tradicional.
- Você provavelmente abandonou o ORM. Isso significa que você está escrevendo sql bruto para todo o código, em vez de deixar o sistema fazer o trabalho por você.
- Você desistiu de fazer atualizações limpas. O que acontece quando o cliente adiciona um campo com um nome que sua próxima versão também usa? No site de matchmaking, a atualização que deseja adicionar um
hasdate
campo para armazenar um registro de data e hora já foi definida como hasdate
um booleano para uma correspondência bem-sucedida ... e sua atualização é interrompida.
- Você confia que o cliente não interrompe o sistema usando uma palavra reservada que também interrompe suas consultas ... em algum lugar.
- Você se vinculou a uma marca de banco de dados. O DDL de diferentes bancos de dados é diferente. Os tipos de banco de dados são o exemplo mais fácil disso.
varchar2
vs text
e similares. Seu código para adicionar a coluna funcionaria no MySQL, mas não no Postgres, Oracle ou SQL Server.
- Você confia que o cliente realmente adicionará bem os dados ? Claro, o EAV está longe de ser ideal, mas agora você tem alguns nomes de tabela obscuros e horrendos que o desenvolvedor não adicionou, com o tipo errado de índice (se houver), sem restrições adicionadas no código onde é necessário seja e assim por diante.
- Você concedeu privilégios de modificação de esquema ao usuário que está executando o aplicativo. Little Bobby Drop Tables não é possível quando você está restrito ao SQL, e não ao DDL (com certeza, você pode fazer isso
delete * from students
, mas não pode realmente atrapalhar o banco de dados de maneiras ruins). O número de coisas que podem dar errado com o acesso ao esquema de um acidente ou de atividades maliciosas disparam.
Isso realmente se resume a "não fazer". Se você realmente deseja isso, siga um padrão conhecido da estrutura da tabela EAV ou um banco de dados totalmente dedicado a essa estrutura. Não permita que as pessoas criem campos arbitrários em uma tabela. As dores de cabeça simplesmente não valem a pena.