Esta questão é sobre como devo projetar um banco de dados, ele pode ser um banco de dados relacional / nosql, dependendo de qual será a melhor solução
Dado um requisito, você precisará criar um sistema que envolva um banco de dados para rastrear "Empresa" e "Usuário". Um único usuário sempre pertence apenas a uma empresa
- Um usuário pode pertencer apenas a uma empresa
- Uma empresa pode ter muitos usuários
O design da tabela "Empresa" é bastante direto. A empresa terá os seguintes atributos / colunas: (vamos simplificar)
ID, COMPANY_NAME, CREATED_ON
Primeiro cenário
Simples e direto, todos os usuários têm o mesmo atributo, portanto, isso pode ser feito facilmente no estilo relacional, tabela de usuários:
ID, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, CREATED_ON
Segundo cenário
O que acontece se diferentes empresas quiserem armazenar atributos de perfil diferentes para seus usuários. Cada empresa terá um conjunto definido de atributos que se aplicariam a todos os usuários dessa empresa.
Por exemplo:
- A empresa A deseja armazenar: LIKE_MOVIE (booleano), LIKE_MUSIC (booleano)
- A empresa B deseja armazenar: FAV_CUISINE (String)
- A empresa C deseja armazenar: OWN_DOG (booleano), DOG_COUNT (int)
Abordagem 1
a maneira da força bruta é ter um esquema único para o usuário e permitir que eles tenham nulos quando não pertencem à empresa:
ID, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, LIKE_MOVIE, LIKE_MUSIC, FAV_CUISINE, OWN_DOG, DOG_COUNT, CREATED_ON
O que é meio desagradável, porque você terá muitos NULLS e linhas de usuário que têm colunas que são irrelevantes para eles (ou seja, todos os usuários pertencentes à empresa A têm valores NULL para FAV_CUISINE, OWN_DOG, DOG_COUNT)
Abordagem 2
uma segunda abordagem, é ter "campo de forma livre":
ID, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, CUSTOM_1, CUSTOM_2, CUSTOM_3, CREATED_ON
O que seria desagradável por si só, já que você não tem idéia do que são campos personalizados, o tipo de dados não refletirá os valores armazenados (por exemplo, armazenaremos o valor int como VARCHAR).
Abordagem 3
Eu examinei o campo JSON do PostgreSQL; nesse caso, você terá:
ID, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, CUSTOM_PROFILE_JSON, CREATED_ON
Nesse caso, como você seria capaz de aplicar esquemas diferentes a um usuário? Um usuário da empresa A terá um esquema parecido com
{"LIKE_MOVIE":"boolean", "LIKE_MUSIC": "boolean"}
Enquanto um usuário com a empresa C terá um esquema diferente:
{"OWN_DOG ":"boolean", "DOG_COUNT": "int"}
Como devo resolver esse problema? Como posso projetar o banco de dados corretamente para permitir esse esquema flexível para um único "objeto" (Usuário) com base no relacionamento que eles têm (Empresa)?
solução relacional? solução nosql?
Editar: Eu também pensei em uma tabela "CUSTOM_PROFILE" que essencialmente armazena atributos do usuário em linhas e não em colunas.
Existem 2 problemas com esta abordagem:
1) Os dados crescem por usuário, à medida que as linhas aumentam, em vez de colunas - e isso significa que, para obter uma imagem completa do usuário, muitas associações precisam ser feitas, várias associações à tabela "perfil personalizado" nos diferentes atributos personalizados
2) O valor dos dados é sempre armazenado como VARCHAR para ser genérico, mesmo se sabemos que os dados devem ser inteiros ou booleanos, etc.
fonte
Respostas:
Por favor, considere isso como uma alternativa. Os dois exemplos anteriores exigirão que você faça alterações no esquema à medida que o escopo do aplicativo aumenta. Além disso, é difícil estender e manter a solução "custom_column". Eventualmente, você terminará com Custom_510 e depois imaginará como essa tabela será péssima.
Primeiro, vamos usar o esquema da sua empresa.
Em seguida, também usaremos o esquema de Usuários para os atributos necessários de nível superior que serão usados / compartilhados por todas as empresas.
Em seguida, criamos uma tabela na qual definiremos nossos atributos dinâmicos específicos aos atributos de usuário personalizados de cada empresa. Portanto, aqui, um valor de exemplo da coluna Attribute seria "LikeMusic":
Em seguida, definimos uma tabela UserAttributes que manterá os valores dos atributos do usuário
Isso pode ser modificado de várias maneiras para melhorar o desempenho. Você pode usar várias tabelas para UserAttributes, tornando cada uma específica para o tipo de dados que está sendo armazenado em Value ou apenas deixá-lo como um VarChar e trabalhar com ele como um armazenamento de valor-chave.
Você também pode querer mover CompanyId da tabela UserAttributeDefiniton e entrar em uma tabela de referência cruzada para provas futuras.
fonte
Use um banco de dados NoSQL. Haveria documentos da empresa e do usuário. Os usuários teriam parte de seu esquema criado dinamicamente com base em um modelo de usuário (texto para indicar campos / tipos para essa empresa.
É assim que pode parecer em Firebase.com. Você teria que aprender como fazê-lo em qualquer que você escolher.
fonte
Se você costuma executar solicitações de campo personalizadas, na verdade, eu o modelo de maneira bastante semelhante ao banco de dados. Crie uma tabela que contém os metadados sobre cada campo personalizado, CompanyCustomField (a quem pertence, o tipo de dados etc.) e outra tabela CompanyCustomFieldValues que contém o CustomerId, FieldId e o valor. Se você estiver usando algo como o Microsoft Sql Server, a coluna de valor será um tipo de dados sql_variant.
Obviamente, isso não é fácil, pois você precisará de uma interface que permita aos administradores definir campos personalizados para cada cliente e outra interface que realmente use esses metadados para criar uma interface do usuário para coletar os valores dos campos. E se você tiver outros requisitos, como o agrupamento de campos ou a necessidade de fazer um tipo de campo da lista de opções, precisará acomodar isso com mais metadados / outras tabelas (por exemplo, CompanyCustomFieldPickListOptions).
Isso não é trivial, mas tem a vantagem de não exigir alterações no banco de dados / alterações de código para cada novo campo personalizado. Quaisquer outros recursos de campos personalizados também precisarão ser codificados (por exemplo, se você deseja regexar validar um valor de sequência, ou permitir apenas datas entre determinados intervalos, ou se você precisa ativar um campo personalizado com base em outro valor de campo personalizado )
fonte
Uma alternativa para as outras respostas é ter uma tabela chamada profile_attrib, ou similar, para que o esquema seja completamente gerenciado pelo seu aplicativo.
À medida que os atributos personalizados são adicionados
ALTER TABLE profile_attrib ADD COLUMN like_movie TINYINT(1)
, você pode proibir a exclusão deles. Isso minimizaria sua associação, ao mesmo tempo em que proporcionaria flexibilidade.Eu acho que a troca de bits é que o aplicativo agora precisa alterar os privilégios da tabela no banco de dados, e você deve ser inteligente ao limpar os nomes das colunas.
fonte
[^\w-]+
deve muito bem fazê-lo, não permitindo nada que não seja -0-9A-Za-z_-
mas sim, higienizar é uma obrigação aqui para se proteger contra malícia ou estupidez.Sua pergunta tem muitas soluções em potencial. Uma solução é armazenar os atributos adicionais como XML. O XML pode ser armazenado como texto ou se você estiver usando um banco de dados que suporte tipos XML como XML (SQL Server). Armazenar como texto limita sua capacidade de consulta (como pesquisar em um atributo personalizado), mas se armazenar e recuperar é tudo o que você precisa, é uma boa solução. Se for necessário consultar, armazenar o XML como um tipo XML seria uma opção melhor (embora isso seja mais específico do fornecedor).
Isso permitirá armazenar qualquer número de atributos em um cliente, basta adicionar uma coluna de adição na tabela do cliente. Pode-se armazenar os atributos como um hashset ou dicionário, perder-se-á a segurança do tipo, pois tudo será uma string para começar, mas se for aplicada uma string de formato padrão para datas, números, booleanos, tudo funcionará bem.
Para maiores informações:
https://msdn.microsoft.com/en-us/library/hh403385.aspx
A resposta de @ WalterMitty também é válida, embora, se houver muitos clientes com atributos diferentes, você possa terminar com muitas tabelas se seguir o modelo de herança. Depende de quantos atributos personalizados são compartilhados entre os clientes.
fonte
Você deve normalizar seu banco de dados para ter 3 tabelas diferentes para cada tipo diferente de perfil da empresa. Usando seu exemplo, você teria tabelas com colunas:
Essa abordagem pressupõe que você conhecerá o formato das informações que uma empresa deseja armazenar antes e que não mudará frequentemente. Se a forma dos dados for desconhecida no momento do design, provavelmente seria melhor usar esse campo JSON ou um banco de dados nosql.
fonte
Por um motivo ou outro, os bancos de dados são o único campo em que o efeito da plataforma interna aparece com mais frequência. Este é apenas mais um caso do anti-padrão surgindo.
Nesse caso, você está tentando combater a solução natural e correta. Os usuários da empresa A não são usuários da empresa B e devem ter suas próprias tabelas para seus próprios campos.
O fornecedor do banco de dados não cobra pela tabela e você não precisa do dobro do espaço em disco para o dobro das tabelas (na verdade, ter duas tabelas é mais eficiente porque você não armazena os atributos de A para os usuários de B. Até mesmo armazenando apenas NULLs ocupa espaço).
Obviamente, se houver campos comuns suficientes, você poderá fatorá-los em uma tabela Usuários compartilhada e ter uma chave estrangeira em cada uma das tabelas de usuários específicas da empresa. Essa é uma estrutura tão simples que nenhum otimizador de consulta de banco de dados luta com ela. Qualquer JOIN necessário é trivial.
fonte
Minha solução pressupõe que você chamaria essa consulta de um programa e deveria poder executar o pós-processamento. Você pode ter as seguintes colunas:
CUSTOM_VALUES será do tipo string armazenando chave e par de valores. chave será o nome da coluna e o valor será o valor da coluna, por exemplo
neste CUSTOM_VALUES, você salvará apenas as informações existentes. Quando você consulta um programa, pode dividir essa sequência e usá-la.
Eu tenho usado essa lógica e ela funciona bem, basta que você aplique a lógica de filtragem no código e não na consulta.
fonte