É uma prática recomendada permitir campos definidos pelo usuário?


17

De um modo geral, é uma prática recomendada permitir campos criados pelo usuário em um banco de dados para um aplicativo da web?

Por exemplo, estou fazendo um webapp de inventário doméstico para minha esposa e ela desejará definir seus próprios campos para itens diferentes. Eu estava planejando permitir que ela criasse categorias de itens e adicionasse "recursos" a essas categorias. Os recursos seriam apenas a chave / valor armazenado como seqüências de caracteres. Dessa forma, se ela tivesse uma categoria chamada "CDs de áudio", por exemplo, ela poderia adicionar recursos para itens como "artista", "faixas" etc. etc. Em outra categoria, como "móveis", ela poderia adicionar recursos para itens como "material "(madeira, plástico, etc). Qualquer item pode pertencer a uma (ou várias) categorias, adicionando esses recursos ao item.

Posso ver problemas em que a pesquisa por esses recursos requer comparações de strings, não há validação de dados etc. Seguindo a metodologia ágil, talvez seja melhor apenas que ela apresente novas categorias e atributos e eu precisaria criar novas tabelas como nós vamos. No meu exemplo, é uma pequena base de usuários (2 de nós) e a quantidade de registros criados seria pequena, portanto não muito ruim.

De um modo geral, como as pessoas lidam com algo assim na "vida real"?


4
Você já pensou em usar um banco de dados orientado a documentos como o MongoDB? Você pode armazenar um documento por tipo que atua como um esquema que também pode ser editado (provavelmente manualmente, dada a pequena escala do projeto).
Andy Hunt

@AndyBursh Um dos bits 'divertidos' do postgres atual é o tipo de dados 'json' ( link ). Essa abordagem permitiria armazenar campos especificados pelo usuário nesses dados, er, documento, er, o que for e, em seguida, usar o restante dos campos para itens com os quais os índices apropriados são semelhantes. Embora tudo isso dependa do uso e seja difícil dizer se isso funcionaria bem para um aplicativo específico ou não. Mas é algo para estar ciente.

tudo: ótima discussão, obrigado por todas as idéias! @AndyBursh Eu já ouvi falar do MongoDB, mas nunca realmente o li. Soa como um outro projeto de casa para experimentar ...
zako42

Respostas:


19

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 attr1fingir que lê artistou tracksou genreou 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 attrtabela. E depois há o thing_attrcom um par de chaves estrangeiras relacionadas à thingmesa e à attrmesa. 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 hasdatecampo para armazenar um registro de data e hora já foi definida como hasdateum 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. varchar2vs texte 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.


4
Você também reinventou o banco de dados.
user253751

1
@immibis, adicionou uma camada na qual o usuário pode administrar sem manipular o restante do banco de dados ou exigir uma reimplementação para atualizar o modelo.

1
@immibis O EAV debate duramente os círculos de bancos de dados relacionais há anos. Em teoria, é desnecessário, mas na prática, você não pode fazer certas coisas sem ele.
31414 Rossi Patterson

1
@ShivanDragon que segue para a abordagem NoSQL. O armazenamento de documentos apenas armazena documentos e não impõe um esquema. Como tal, adicionar e remover campos e analisar os documentos está completamente fora do escopo do próprio banco de dados (e você escreveu seu modelo para acomodar isso). É um conjunto completamente diferente de compromissos do que o banco de dados relacional para uma estrutura EAV.

1
Relacionados discussão do bate-papo: em alternativas de design de banco de dados para EAV

5

Fazer isso bem é difícil.

Para um aplicativo único como o que você está planejando, é possível adicionar uma coluna para cada campo e fornecer uma interface do usuário que torne a definição de campo por usuários não treinados mais segura do que fornecer a eles uma linha de comando SQL. Ou você pode seguir o temido padrão Entidade-Atributo-Valor , que é uma resposta clássica, embora um tanto assustadora, a esse tipo de problema. Construindo a UI para definir campos EAV é geralmente muito mais complexo do que para as colunas de banco de dados, e as consultas pode ficar muito peludo, mas um grande número de campos ( ou seja , schemata altamente esparsa da matriz), pode ser a única maneira de obter o trabalho feito.


Em resumo: pequeno projeto == KISS. Ágil até o chão.
Encaitar

O problema com as atualizações da tabela do banco de dados é que, dependendo da quantidade de dados e dos índices necessários (os campos personalizados geralmente exigem recursos de pesquisa), a consulta de alteração da tabela pode demorar bastante tempo. Para encurtar a história, o MySQL e outros bancos de dados relacionais simplesmente não são um bom meio para esse tipo de requisito.
Oddman

0

Eu vim uma cruz algo parecido recentemente.

Eu fiz 2 mesas.

1: table Objects 
    Id , name, type

Ele é todos os seus objetos. Você definiu o nome dele.

E um tipo desse objeto: - para mim, os tipos disponíveis eram inventário, inventário, item.

E a configuração usual era que n itens são filhos ou inventário, que também é filho do cargo e eu usei uma tabela de junção para juntar objetos uns aos outros

2 table settings 
     organization_Id , title, value , type

A tabela de configurações contém todos os nomes de campos para esse tipo de objeto específico e o valor em valor.

Exemplo de propriedades do escritório

Localização, telefone, horário de trabalho

E para itens

  • Montante
  • Preço
  • Código de barras

Etc, todas essas propriedades são impostas pelo modelo e salvas na tabela de configurações como linhas separadas (ainda assim, use replace not insert para evitar várias linhas no mesmo campo)

Então, sempre que eu quero um escritório, carrego-o facilmente com todas as suas relações e configurações em que as configurações object_I'd (objetos solicitados)

Depois disso, giro todas as linhas das configurações e é isso.

E, no caso de eu querer que uma configuração fosse específica para um item de um inventário (não global), defino object_I'd = da tabela de relações object_objects e defino settings.type = relationship_setting

Espero que você entenda o que quero dizer. Tentarei reformatar a resposta quando chegar a um laptop


2
Dica profissional - não publique neste fórum a partir do seu telefone. A correção automática torna partes da sua postagem ilegíveis.
BobDalgleish

Haha bom observação :)
Zalaboza

0

É uma prática recomendada permitir campos definidos pelo usuário?

Não, não é uma prática ruim. Isso é bastante comum. Em termos de OO, isso é chamado de herança. Você tem um inventário de classe base e duas classes herdadas de AudioCD e móveis.

De um modo geral, como as pessoas lidam com algo assim na "vida real"?

Você precisa decidir como o item de inventário, o CD de áudio e os móveis são armazenados no banco de dados.

Se a consulta fácil é mais importante para você e o db-space / normalization não importa, você implementaria o esquema "tabela por hierarquia".

Se o espaço / normalização for mais importante para você e as consultas mais complicadas não forem um problema, você implementaria o esquema "tabela por tipo".

Para obter mais detalhes, consulte herança dotnet tabela por tipo vs tabela por hierarquia ou herança de hibernação java .


Não sei se isso aborda a questão. O usuário não está modificando o código para criar novas classes
Colin D
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.