Como criar um banco de dados para campos definidos pelo usuário?

145

Meus requisitos são:

  • Precisa ser capaz de adicionar dinamicamente campos definidos pelo usuário de qualquer tipo de dados
  • Precisa ser capaz de consultar UDFs rapidamente
  • Precisa ser capaz de fazer cálculos em UDFs com base no tipo de dados
  • Precisa ser capaz de classificar UDFs com base no tipo de dados

Outra informação:

  • Estou procurando desempenho principalmente
  • Existem alguns milhões de registros mestre que podem ter dados UDF anexados
  • Quando eu verifiquei pela última vez, havia mais de 50mil de registros UDF em nosso banco de dados atual
  • Na maioria das vezes, uma UDF é anexada apenas a alguns milhares de registros mestre, nem todos eles
  • UDFs não são unidos ou usados ​​como chaves. São apenas dados usados ​​para consultas ou relatórios

Opções:

  1. Crie uma tabela grande com StringValue1, StringValue2 ... IntValue1, IntValue2, ... etc. Eu odeio essa idéia, mas a considerarei se alguém puder me dizer que é melhor do que outras idéias e por quê.

  2. Crie uma tabela dinâmica que adicione uma nova coluna sob demanda, conforme necessário. Também não gosto dessa ideia, pois sinto que o desempenho seria lento, a menos que você indexasse todas as colunas.

  3. Crie uma única tabela contendo UDFName, UDFDataType e Value. Quando um novo UDF é adicionado, gere uma View que extraia apenas esses dados e os analise em qualquer tipo especificado. Os itens que não atendem aos critérios de análise retornam NULL.

  4. Crie várias tabelas UDF, uma por tipo de dados. Portanto, teríamos tabelas para UDFStrings, UDFDates, etc. Provavelmente, faria o mesmo que o número 2 e geraria automaticamente uma visualização a qualquer momento em que um novo campo fosse adicionado

  5. DataTypes XML? Eu não trabalhei com isso antes, mas já os vi mencionados. Não tenho certeza se eles me dariam os resultados que eu quero, especialmente com o desempenho.

  6. Algo mais?

Rachel
fonte
7
Martin Fowler recomenda 2 (esquema atualizável pelo usuário) ou 5 (LOB XML indexado): martinfowler.com/bliki/UserDefinedField.html
Neil McGuigan
Consulte também a pergunta StackOverflow sobre esquemas de banco de dados dinâmicos .
FloverOwe # 8/19

Respostas:

49

Se o desempenho é a principal preocupação, eu recomendaria o número 6 ... uma tabela por UDF (realmente, essa é uma variante do número 2). Esta resposta é especificamente adaptada a esta situação e à descrição dos padrões de acesso e distribuição de dados descritos.

Prós:

  1. Como você indica que alguns UDFs têm valores para uma pequena parte do conjunto de dados geral, uma tabela separada forneceria o melhor desempenho, pois essa tabela terá apenas o tamanho necessário para suportar o UDF. O mesmo vale para os índices relacionados.

  2. Você também obtém um aumento de velocidade limitando a quantidade de dados que precisam ser processados ​​para agregações ou outras transformações. Dividir os dados em várias tabelas permite executar algumas análises estatísticas agregadas e outras sobre os dados UDF e associar esse resultado à tabela mestre por meio de chave estrangeira para obter os atributos não agregados.

  3. Você pode usar nomes de tabela / coluna que refletem quais são realmente os dados.

  4. Você tem controle completo para usar tipos de dados, verificar restrições, valores padrão etc. para definir os domínios de dados. Não subestime o impacto no desempenho resultante da conversão instantânea de tipos de dados. Essas restrições também ajudam os otimizadores de consulta do RDBMS a desenvolver planos mais eficazes.

  5. Se você precisar usar chaves estrangeiras, a integridade referencial declarativa interna raramente é executada pela imposição de restrições no nível do aplicativo ou baseada em acionador.

Contras:

  1. Isso pode criar muitas tabelas. A imposição da separação de esquemas e / ou de uma convenção de nomenclatura aliviaria isso.

  2. É necessário mais código de aplicativo para operar a definição e o gerenciamento de UDF. Espero que ainda seja necessário menos código do que para as opções originais 1, 3 e 4.

Outras considerações:

  1. Se houver algo sobre a natureza dos dados que faça sentido para as UDFs serem agrupadas, isso deve ser incentivado. Dessa forma, esses elementos de dados podem ser combinados em uma única tabela. Por exemplo, digamos que você tenha UDFs para cores, tamanhos e custos. A tendência nos dados é que a maioria das instâncias desses dados se parece com

     'red', 'large', 45.03 

    ao invés de

     NULL, 'medium', NULL

    Nesse caso, você não sofrerá uma penalidade de velocidade perceptível combinando as 3 colunas em 1 tabela, porque poucos valores seriam NULL e você evita criar mais 2 tabelas, o que significa 2 menos junções necessárias quando você precisa acessar todas as 3 colunas .

  2. Se você atingir uma parede de desempenho de uma UDF que é altamente preenchida e usada com frequência, isso deve ser considerado para inclusão na tabela mestre.

  3. O design da tabela lógica pode levá-lo a um determinado ponto, mas quando a contagem de registros se torna realmente grande, você também deve começar a analisar quais opções de particionamento de tabela são fornecidas pelo seu RDBMS de sua escolha.

Phil Helmer
fonte
1
Lista de verificação! Piada interna entre mim e Phil, espero que não seja contra as regras.
precisa saber é o seguinte
Obrigado, acho que vou fazer alguma variação disso. A maioria dos nossos dados UDF vem de campos de importação não mapeados que precisam permanecer apenas para fins de referência, portanto, eu gostaria de colocá-los em uma tabela. Outras UDFs são definidas conforme necessário (não consigo identificá-las com antecedência .. elas geralmente são criadas quando alteramos algum processo ou decidimos rastrear algo especial por alguns meses) e são comumente usadas em consultas. Acho que vou fazer uma tabela separada para cada unidade lógica desses valores.
Rachel
Estou trabalhando com uma tabela datada / com versão de UDFs, uso esse método, stackoverflow.com/a/123481/328968 , para obter os valores mais recentes.
Peter
22

Eu tenho escrito sobre este problema muito . A solução mais comum é o antipadrão de Entidade-Atributo-Valor, que é semelhante ao que você descreve em sua opção nº 3. Evite esse design como uma praga .

O que eu uso para esta solução quando preciso de campos personalizados verdadeiramente dinâmicos é armazená-los em um blob de XML, para que eu possa adicionar novos campos a qualquer momento. Mas, para agilizar, crie também tabelas adicionais para cada campo em que você precisa pesquisar ou classificar (você não cria uma tabela por campo - apenas uma tabela por campo pesquisável ). Isso às vezes é chamado de design de índice invertido.

Você pode ler um artigo interessante de 2009 sobre esta solução aqui: http://backchannel.org/blog/friendfeed-schemaless-mysql

Ou você pode usar um banco de dados orientado a documentos, onde é esperado que você tenha campos personalizados por documento. Eu escolheria Solr .

Bill Karwin
fonte
1
Você pode explicar por que devo evitar a opção 3? Eu olhei para alguns de seus exemplos, mas eles realmente não são iguais ao que estou tentando fazer. Eu simplesmente quero um local para armazenar dados extras, não um local para armazenar todos os atributos.
Rachel
2
Para iniciantes, quem você faria com um atributo NOT NULL? Como você tornaria um atributo UNIQUE sem tornar todos os atributos UNIQUE? Isso continua a partir daí. Você acaba escrevendo o código do aplicativo para fornecer recursos que o RDBMS já fornece para você, até o ponto de precisar escrever algum tipo de classe de mapeamento para simplesmente inserir um registro lógico de entidade e recuperá-lo.
Bill Karwin
2
A resposta curta é "não misture dados e metadados". Criar colunas varchar para fieldnameou tablenameestá armazenando identificadores de metadados como cadeias de dados, e esse é o começo de muitos problemas. Veja também en.wikipedia.org/wiki/Inner-platform_effect
Bill Karwin
2
@ Thomas: No design de índice invertido, você pode usar soluções de esquema padrão para tipos de dados e restrições como UNIQUE e FOREIGN KEY. Eles não funcionam quando você usa o EAV. Concordo que o índice invertido compartilha com o EAV a característica de não ser relacional simplesmente porque suporta atributos diferentes por linha, mas é um ponto de comprometimento.
Bill Karwin
2
@thitami, o que aprendi ao longo dos anos é que qualquer solução pode ser a certa para o seu aplicativo. Até o EAV pode ser a solução menos ruim para um aplicativo específico. Você não pode escolher uma estratégia de otimização sem conhecer suas consultas. Todo tipo de otimização aprimora determinadas consultas em detrimento de outras consultas.
Bill Karwin
10

Eu provavelmente criaria uma tabela da seguinte estrutura:

  • Nome varchar
  • Tipo varchar
  • decimal NumberValue
  • varchar StringValue
  • date DateValue

Os tipos exatos de curso dependem de suas necessidades (e, é claro, dos dbms que você está usando). Você também pode usar o campo NumberValue (decimal) para int e booleanos. Você pode precisar de outros tipos também.

Você precisa de algum link para os registros mestre que possuem o valor. Provavelmente é mais fácil e rápido criar uma tabela de campos do usuário para cada tabela mestre e adicionar uma chave estrangeira simples. Dessa forma, você pode filtrar os registros mestre pelos campos do usuário com facilidade e rapidez.

Você pode querer ter algum tipo de informação de metadados. Então você acaba com o seguinte:

Tabela UdfMetaData

  • int id
  • Nome varchar
  • Tipo varchar

Table MasterUdfValues

  • int Master_FK
  • int MetaData_FK
  • decimal NumberValue
  • varchar StringValue
  • date DateValue

Faça o que fizer, eu não mudaria a estrutura da tabela dinamicamente. É um pesadelo de manutenção. Eu também não usaria estruturas XML, elas são muito lentas.

Stefan Steinegger
fonte
Gosto da sua estratégia e, talvez, opte por ela, mas em 2017, você optará por algo diferente? como json
maztt 28/02
Em nosso projeto, implementamos nossas próprias estruturas de dados que serializam para algo semelhante ao json. Possui uma interface tipesave para ler e gravar dados sem conversão e com ótima integração da linguagem de programação. Isso é realmente bom. Ele tem o mesmo problema que todo esse tipo de "documentos" nos bancos de dados. É difícil consultar valores específicos e não pode facilmente referenciar dados fora do "documento". Dependendo do uso, ambos não são um problema.
Stefan Steinegger
Além disso, o que propus em 2011 é o IMHO ainda uma solução válida.
Stefan Steinegger
10

Isso soa como um problema que pode ser melhor resolvido por uma solução não relacional, como MongoDB ou CouchDB.

Ambos permitem a expansão dinâmica do esquema e permitem manter a integridade da tupla que você procura.

Eu concordo com Bill Karwin, o modelo EAV não é uma abordagem de alto desempenho para você. O uso de pares nome-valor em um sistema relacional não é intrinsecamente ruim, mas só funciona bem quando o par nome-valor cria uma tupla completa de informações. Ao usá-lo obriga a reconstruir dinamicamente uma tabela em tempo de execução, todos os tipos de coisas começam a ficar difíceis. A consulta se torna um exercício de manutenção de pivô ou obriga a empurrar a reconstrução da tupla para a camada de objeto.

Você não pode determinar se um valor nulo ou ausente é uma entrada válida ou falta de entrada sem incorporar regras de esquema em sua camada de objeto.

Você perde a capacidade de gerenciar com eficiência seu esquema. Um varchar de 100 caracteres é o tipo certo para o campo "value"? 200 caracteres? Em vez disso, deveria ser nvarchar? Pode ser uma troca difícil e termina com você ter que colocar limites artificiais na natureza dinâmica do seu aparelho. Algo como "você pode ter apenas x campos definidos pelo usuário e cada um pode ter apenas y caracteres.

Com uma solução orientada a documentos, como MongoDB ou CouchDB, você mantém todos os atributos associados a um usuário em uma única tupla. Como as junções não são um problema, a vida é feliz, pois nenhuma dessas duas se dá bem com as junções, apesar do hype. Seus usuários podem definir quantos atributos quiserem (ou você permitirá) por períodos que não serão difíceis de gerenciar até atingir cerca de 4 MB.

Se você tiver dados que exijam integridade no nível do ACID, considere dividir a solução, com os dados de alta integridade que vivem em seu banco de dados relacional e os dados dinâmicos em um armazenamento não relacional.

Data Monk
fonte
6

Mesmo se você fornecer um usuário adicionando colunas personalizadas, não será necessariamente o caso de a consulta nessas colunas ter um bom desempenho. Há muitos aspectos que entram no design de consultas que permitem um bom desempenho, o mais importante deles é a especificação adequada sobre o que deve ser armazenado em primeiro lugar. Portanto, fundamentalmente, você deseja permitir que os usuários criem esquemas sem pensar nas especificações e conseguir derivar rapidamente informações desse esquema? Nesse caso, é improvável que qualquer solução desse tipo seja dimensionada bem, especialmente se você quiser permitir que o usuário faça análises numéricas nos dados.

Opção 1

Na IMO, essa abordagem fornece um esquema sem o conhecimento do significado do esquema, que é uma receita para um desastre e um pesadelo para os designers de relatórios. Ou seja, você deve ter os metadados para saber qual coluna armazena quais dados. Se esses metadados forem confusos, ele poderá potencializar seus dados. Além disso, facilita colocar os dados errados na coluna errada. ("O quê? String1 contém o nome de conventos? Eu pensei que eram as drogas favoritas de Chalie Sheen.")

Opção 3,4,5

Os requisitos 2, 3 e 4 da IMO eliminam qualquer variação de um EAV. Se você precisar consultar, classificar ou fazer cálculos com esses dados, um EAV é o sonho de Cthulhu e o pesadelo de sua equipe de desenvolvimento e do DBA. Os EAVs criarão um gargalo em termos de desempenho e não fornecerão a integridade dos dados necessários para obter rapidamente as informações desejadas. As consultas rapidamente se transformarão em nós górdio de crosstab.

Opção 2,6

Isso realmente deixa uma escolha: reunir especificações e criar o esquema.

Se o cliente deseja o melhor desempenho nos dados que deseja armazenar, ele precisa passar pelo processo de trabalhar com um desenvolvedor para entender suas necessidades, para que ele seja armazenado da maneira mais eficiente possível. Ele ainda pode ser armazenado em uma tabela separada do restante das tabelas com código que cria dinamicamente um formulário com base no esquema da tabela. Se você tiver um banco de dados que permita propriedades estendidas nas colunas, poderá usá-las para ajudar o construtor de formulários a usar rótulos, dicas de ferramentas etc. para que tudo o que seja necessário seja adicionar o esquema. De qualquer maneira, para criar e executar relatórios com eficiência, os dados precisam ser armazenados corretamente. Se os dados em questão tiverem muitos valores nulos, alguns bancos de dados poderão armazenar esse tipo de informação. Por exemplo,

Se esse fosse apenas um conjunto de dados nos quais nenhuma análise, filtragem ou classificação deveria ser feita, eu diria que alguma variação de um EAV pode ajudar. No entanto, considerando seus requisitos, a solução mais eficiente será obter as especificações adequadas, mesmo se você armazenar essas novas colunas em tabelas separadas e criar formulários dinamicamente a partir dessas tabelas.

Colunas esparsas

Thomas
fonte
5
  1. Crie várias tabelas UDF, uma por tipo de dados. Portanto, teríamos tabelas para UDFStrings, UDFDates, etc. Provavelmente, faria o mesmo que o número 2 e geraria automaticamente uma visualização a qualquer momento em que um novo campo fosse adicionado

De acordo com minha pesquisa, várias tabelas baseadas no tipo de dados não ajudarão você no desempenho. Especialmente se você tiver dados em massa, como 20K ou 25K com mais de 50 UDFs. O desempenho foi o pior.

Você deve ir com uma única tabela com várias colunas, como:

varchar Name
varchar Type
decimal NumberValue
varchar StringValue
date DateValue
Amit Contractor
fonte
Este deve ser um correto e votado. A resposta anterior em 2011 por Phil não é mais um bom conselho hoje em 2016.
Yap Kai Lun Leon 24/03
Posso obter um exemplo simples de como fazer esse processo no sql.?
Niroj 16/08/16
Desculpe pela resposta tardia, mas você deseja que a estrutura do banco de dados seja a mesma. Eu não te entendi @Niroj. Você pode explicar em detalhes como o que deseja.
Amit Contractor
4

Esta é uma situação problemática e nenhuma das soluções parece "correta". No entanto, a opção 1 é provavelmente a melhor, tanto em termos de simplicidade quanto em termos de desempenho.

Essa também é a solução usada em alguns aplicativos empresariais comerciais.

EDITAR

outra opção que está disponível agora, mas não existia (ou pelo menos não estava madura) quando a pergunta foi feita originalmente é usar os campos json no banco de dados.

muitos bancos de dados relacionais agora suportam campos baseados em json (que podem incluir uma lista dinâmica de subcampos) e permitem a consulta neles

postgress

mysql

Ophir Yoktan
fonte
1
Eu odeio a idéia de criar possivelmente centenas de colunas não utilizadas. Isso vai contra o que aprendi e li sobre o design de banco de dados SQL. No momento, temos mais de 1300 valores definidos pelo usuário, embora muitos deles sejam simplesmente duplicados de itens existentes com nomes diferentes.
24411 Rachel
1300 UDF diferente para uma única tabela? cada usuário tem a opção de adicionar UDF ou apenas algum tipo de usuário avançado?
Ophir Yoktan
Faz parte do processo de importação ... ele adiciona quaisquer dados não mapeados a um campo definido pelo usuário. Como ninguém leva tempo para mapear dados não mapeados para os campos UDF existentes, ele apenas cria novos e, ao longo dos anos, muitos foram adicionados.
24411 Rachel
2

Eu tive experiência ou 1, 3 e 4 e todos acabam confusos, com não ficando claro quais são os dados ou são realmente complicados com algum tipo de categorização simples para decompor os dados em tipos dinâmicos de registro.

Eu ficaria tentado a experimentar XML, você poderá aplicar esquemas contra o conteúdo do xml para verificar a digitação de dados etc., o que ajudará a manter conjuntos diferentes de dados UDF. Nas versões mais recentes do SQL Server, você pode indexar nos campos XML, o que deve ajudar no desempenho. (consulte http://blogs.technet.com/b/josebda/archive/2009/03/23/sql-server-2008-xml-indexing.aspx ) por exemplo

Jon Egerton
fonte
Honestamente, eu não procurei em XML. A principal desvantagem para isso é que eu teria que aprender como funcionava e como consulta contra ele, e eu ouvi o desempenho pode ser pior do que as outras opções
Rachel
1
Eu evitaria usar xml para isso: ele pode fazer o trabalho, e eu implementei algo assim em xml no passado, mas o desempenho ficou muito ruim à medida que as estruturas de dados cresciam e a complexidade do código era alta.
Kell
2

Se você estiver usando o SQL Server, não negligencie o tipo sqlvariant. É muito rápido e deve fazer o seu trabalho. Outros bancos de dados podem ter algo semelhante.

Os tipos de dados XML não são tão bons por razões de desempenho. Se você estiver fazendo cálculos no servidor, precisará constantemente desserializá-los.

A opção 1 parece ruim e parece grosseira, mas em termos de desempenho pode ser sua melhor aposta. Eu criei tabelas com colunas denominadas Field00-Field99 antes, porque você simplesmente não consegue superar o desempenho. Pode ser necessário considerar também o desempenho do INSERT; nesse caso, esse também é o caminho a ser seguido. Você sempre pode criar modos de exibição nesta tabela se quiser que ele fique bonito!

Tim Rogers
fonte
Obrigado, vou dar uma outra olhada nas variantes SQL. Minha maior preocupação é o desempenho e eu não tenho certeza de como ele iria lidar com isso, especialmente se estamos falando de linhas 50mil mais
Rachel
Acabei de descobrir que sql_varients não pode ser usado com a cláusula LIKE ... isso é uma grande desvantagem para mim. Claro, se eu criar uma visão para cada UDF então eu poderia lançá-lo para o tipo de dados apropriado com base no SQL_VARIANT_PROPERTY (valor, 'BaseType') ... ainda assim, parece que é ruim para o desempenho
Rachel
Você pode usar LIKE, mas precisa converter o valor primeiro. O LIKE só funciona em varchars, então você deve converter sua variável sql_variant em um varchar. Desde que você saiba se o seu UDF é um varchar (por exemplo, porque o tipo está armazenado em outro lugar), você pode filtrar todas as suas linhas para varchars, em seguida, converter e executar sua consulta LIKE: por exemplo. selecione * FROM MinhaTabela onde variant_type = 'v' Cast (variant_value como varchar (max)) LIKE 'Blah%' Dessa forma, você não está convertendo ints e assim por diante em strings que o atrasariam.
Tim Rogers
Eu precisaria executar alguns testes para ver como o desempenho está nisso, especialmente com milhões de linhas. Conhece algum artigo on-line sobre desempenho usando sql_varients? Especialmente com elenco e número muito grande de registros?
Rachel
1

O SharePoint usa a opção 1 e tem um desempenho razoável.

Nathan DeWitt
fonte
1

Eu consegui isso com muito sucesso no passado, usando nenhuma dessas opções (opção 6? :)).

Eu crio um modelo para os usuários brincarem (armazenar como xml e expor por meio de uma ferramenta de modelagem personalizada) e a partir das tabelas e visualizações geradas pelo modelo para unir as tabelas base às tabelas de dados definidas pelo usuário. Portanto, cada tipo teria uma tabela base com dados principais e uma tabela de usuário com campos definidos pelo usuário.

Tome um documento como exemplo: campos típicos seriam nome, tipo, data, autor, etc. Isso seria exibido na tabela principal. Os usuários definiriam seus próprios tipos de documentos especiais com seus próprios campos, como contract_end_date, renewal_clause, blá blá blá. Para esse documento definido pelo usuário, haveria a tabela principal de documentos, a tabela xcontract, unida em uma chave primária comum (portanto, a chave primária xcontracts também é estrangeira na chave primária da tabela principal). Então eu geraria uma visualização para agrupar essas duas tabelas. O desempenho ao consultar foi rápido. regras de negócios adicionais também podem ser incorporadas às visualizações. Isso funcionou muito bem para mim.

Kell
fonte
1

Nosso banco de dados fornece um aplicativo SaaS (software de helpdesk) em que os usuários têm mais de 7k "campos personalizados". Utilizamos uma abordagem combinada:

  1. (EntityID, FieldID, Value)tabela para pesquisar os dados
  2. um campo JSON na entitiestabela, que contém todos os valores da entidade, usados ​​para exibir os dados. (dessa forma, você não precisa de um milhão de JOIN para obter os valores dos valores).

Você pode dividir ainda mais o número 1 para ter uma "tabela por tipo de dados", como sugere esta resposta , dessa maneira você pode até indexar suas UDFs.

PS Algumas palavras para defender a abordagem "Entidade-Atributo-Valor" que todo mundo continua contestando. Usamos o número 1 sem o número 2 por décadas e funcionou muito bem. Às vezes é uma decisão de negócios. Você tem tempo para reescrever seu aplicativo e redesenhar o banco de dados ou pode gastar alguns dólares em servidores em nuvem, que são realmente baratos hoje em dia? A propósito, quando estávamos usando a abordagem nº 1, nosso banco de dados possuía milhões de entidades, acessadas por centenas de milhares de usuários, e um servidor db de núcleo duplo de 16 GB estava indo muito bem

Alex
fonte
Oi @ Alex, me deparei com um problema semelhante. Se bem entendi, você tem: 1) uma custom_fieldstabela que armazena valores como 1 => last_concert_year, 2 => band, 3 => musice, em seguida, uma custom_fields_valuestabela com valores 001, 1, 1976 002, 1, 1977 003, 2, Iron Maiden003, 3 , Metal Espero que o exemplo faça sentido para você e desculpe pela formatação!
Thitami
@thitami não exatamente. Seguindo seu exemplo: Eu tenho uma bandstabela com uma linha 1,'Iron Maiden', em seguida, custom_fieldscom linhas 1,'concert_year' | 2,'music', em seguida, custom_fields_valuescom filas1,1,'1977'|1,2,'metal'
Alex
0

Nos comentários, vi você dizendo que os campos UDF devem despejar dados importados que não são mapeados corretamente pelo usuário.

Talvez outra opção seja rastrear o número de UDFs feitas por cada usuário e forçá-los a reutilizar campos dizendo que eles podem usar 6 (ou algum outro limite igualmente aleatório) no máximo de campos personalizados.

Quando você se deparar com um problema de estruturação de banco de dados como esse, geralmente é melhor voltar ao design básico do aplicativo (sistema de importação no seu caso) e colocar mais algumas restrições nele.

Agora, o que eu faria é a opção 4 (EDIT) com a adição de um link para os usuários:

general_data_table
id
...


udfs_linked_table
id
general_data_id
udf_id


udfs_table
id
name
type
owner_id --> Use this to filter for the current user and limit their UDFs
string_link_id --> link table for string fields
int_link_id
type_link_id

Agora, certifique-se de fazer visualizações para otimizar o desempenho e obter seus índices corretos. Esse nível de normalização diminui o tamanho do banco de dados, mas seu aplicativo é mais complexo.

Wouter Simons
fonte
0

Eu recomendaria o número 4, pois esse tipo de sistema foi usado no Magento, que é uma plataforma CMS de comércio eletrônico altamente credenciada. Use uma única tabela para definir seus campos personalizados usando as colunas fieldId e label . Em seguida, tenha tabelas separadas para cada tipo de dados e, dentro de cada uma dessas tabelas, tenha um índice que indexe por fieldId e as colunas de valor do tipo de dados . Em seguida, em suas consultas, use algo como:

SELECT *
FROM FieldValues_Text
WHERE fieldId IN (
    SELECT fieldId FROM Fields WHERE userId=@userId
)
AND value LIKE '%' + @search + '%'

Isso garantirá o melhor desempenho possível para tipos definidos pelo usuário, na minha opinião.

Na minha experiência, trabalhei em vários sites Magento que atendem a milhões de usuários por mês, hospeda milhares de produtos com atributos personalizados e o banco de dados lida com a carga de trabalho com facilidade, mesmo para geração de relatórios.

Para gerar relatórios, você pode usar PIVOTpara converter os valores dos rótulos da tabela Fields em nomes de colunas e dinamizar os resultados da consulta de cada tabela de tipo de dados nessas colunas dinâmicas.

Mark Entingh
fonte