Dados de configuração: tabela de linha única versus tabela de pares nome-valor


64

Digamos que você escreva um aplicativo que possa ser configurado pelo usuário. Para armazenar esses "dados de configuração" em um banco de dados, dois padrões são comumente usados.

  1. A tabela de linha única

      CompanyName  |  StartFullScreen  |  RefreshSeconds  |  ...
    ---------------+-------------------+------------------+--------
      ACME Inc.    |        true       |       20         |  ...
    
  2. A tabela nome-valor-par

      ConfigOption   |   Value
    -----------------+-------------
     CompanyName     | ACME Inc.
     StartFullScreen | true (or 1, or Y, ...)
     RefreshSeconds  | 20
     ...             | ...
    

Eu já vi as duas opções na natureza e ambas têm vantagens e desvantagens óbvias, por exemplo:

  • As tabelas de linha única limitam o número de opções de configuração que você pode ter (já que o número de colunas em uma linha geralmente é limitado). Cada opção de configuração adicional requer uma alteração no esquema do banco de dados.
  • Em uma tabela nome-valor-par, tudo é "tipicamente digitado" (você precisa codificar / decodificar seus parâmetros Booleanos / Data / etc.).
  • (muito mais)

Existe algum consenso na comunidade de desenvolvimento sobre qual opção é preferível?


2
Não há razão para que a abordagem "vertical" não possa ter tipos de dados diferentes. Adicione uma coluna int, float e texto por linha. Salve / carregue valores usando funções específicas de tipo, como 'SaveConfigInt (' field ', n)' '
GrandmasterB

4
Há uma excelente pergunta sobre o StackOverflow perguntando sobre isso, e a resposta principal fornece prós e contras para ambas as abordagens. stackoverflow.com/questions/2300356/…
Kevin

11
Abordagem 3: Coluna Única / Linha Única com um formato simples de intercâmbio de dados como JSON ou YAML. Combina vantagens de ambas as abordagens.
schlamar

que tal usar uma tabela de linha única com dados complexos contendo xml / json, como <config> <CompanyName> ACME Inc. </CompanyName> <StartFullScreen> true </StartFullScreen>20<RefreshSeconds></RefreshSeconds> </config> e validar objeto na camada de negócios?
John John

11
@ John: Boa idéia, se estruturas hierárquicas são necessárias. Caso contrário, é apenas a opção 2 com maior complexidade.
Heinzi 28/02

Respostas:


15

Pessoalmente, prefiro as tabelas de linha única para a maioria das coisas. Embora seja verdade que é menos flexível, a menos que você esteja esperando um comportamento dinâmico, é perfeitamente aceitável adicionar colunas extras mais tarde, se necessário. De certa forma, é o equivalente a usar um dicionário / mapa para armazenar pares nome-valor versus ter membros de classe durante a programação. É verdade que não é uma metáfora perfeita, mas muitas das vantagens e desvantagens são paralelas quando você pensa sobre isso.

Então você usaria um dicionário / mapa sobre os alunos? Provavelmente não, a menos que você tenha motivos para pensar que a quantidade de dados a ser representada é totalmente adaptável, assim como ter uma tabela de pares nome-valor.


E se os dados a serem armazenados forem definidos pelo usuário? ou seja, pense em uma interface do usuário em que o usuário possa criar um "campo" especificando o rótulo do campo, o tipo de dados que ele conterá etc. Isso significaria a execução de instruções DDL a partir do código. Você continuaria com a opção 1?
devanalyst

11
@devanalyst Não, se os dados pudessem mudar de componente para componente, não faria sentido tentar criar uma tabela estática para representá-lo. Nesse caso, seria melhor usar a segunda opção.
Neil

12

Geralmente eu usaria a opção 2, mas teria várias colunas para aplicar o tipo de dados

ConfigOption   |   textValue    |   DateValue   |   NumericValue

A opção 1 possui o benefício adicional de que você pode facilmente "trocar" configurações inteiras adicionando uma Activecoluna.


Se você deseja permitir que as configurações sejam desativadas (para a opção 1), faça pelo menos um activatedOncarimbo de data e hora, para saber quando foi ativado. E se você optar pela opção 2 ... o que acontece se o resultado final for armazenar valores em várias colunas (ou é oracle, onde (aparentemente) nulo e uma string vazia são equivalentes)?
Clockwork-Muse

11
@ X-Zero, o armazenamento de várias configurações geralmente é feito para fins de teste, mas um carimbo de hora não pode prejudicar. O Maintenance Config, ligar para obter o valor saberia o que coluna para verificar, se você realmente quiser, você pode adicionar uma coluna para o tipo de dados .. Mas eu acho que é sobre matar ...
Morons

5
um esquema EATV (Entity-Attribute-Type-Value) quebra a terceira forma normal; a coluna Tipo está indiretamente relacionada apenas à chave primária da tabela, através da coluna Valor, que a coluna Tipo descreve. Além disso, o armazenamento e instanciação dinâmicos não resolvem muito; se um método GetConfigValue () puder retornar qualquer tipo, ele deve retornar Object (ou receber o tipo esperado de alguma forma) que ainda deve ser avaliado em tempo de execução.
Keith

5
Toda vez que a opção 1 foi implementada no software que eu vi, ela teve que ser convertida na opção 2. A opção 2 é mais fácil de manter ao longo do tempo, leva apenas um tempo para ser implementada corretamente na primeira vez. A opção 1 é rápida e fácil de implementar, mas a manutenção ao longo do tempo é horrível, a menos que seu software seja pequeno, sem chance de crescimento.
Jimmy Hoffa

8

Para mim, se você usa uma única linha ou EAV, depende de como você deseja consumi-los.

O poder do EAV é que novos dados possam ser adicionados sem alterações na estrutura. Isso significa que, se você deseja um novo valor de configuração, basta adicioná-lo à tabela e retirá-lo onde deseja no código, e não precisa adicionar um novo campo ao domínio, esquema, mapeamento, consultas DAL etc.

Sua falha é que ele possui apenas a estrutura mais simples, exigindo que você lide com os dados de maneira pessimista. Todo uso de qualquer valor de configuração deve esperar que o valor não esteja presente ou não esteja no formato adequado e se comporte de acordo quando não estiver. Um valor de configuração pode não ser analisável para um double, int, ou char. Pode ser nulo. pode não haver linha para o valor. As maneiras de contornar isso geralmente exigem que exista um único valor "padrão" válido para todos os valores de configuração de um tipo de código em particular ( extremamente raro; mais frequentemente, o valor padrão é tão problemático para consumir código quanto nenhum), ou mantenha um dicionário codificado de valores padrão (que deve mudar toda vez que uma nova coluna é adicionada, tornando a principal vantagem do armazenamento EAV bastante discutível).

Uma única linha larga é praticamente o oposto. Você o mapeia para uma única instância de um objeto Configuration com um campo / propriedade para cada valor de configuração existente. Você sabe exatamente que tipo esses valores devem ser no momento da compilação e "falha rapidamente" no DAL se uma coluna de configuração não existir ou não tiver um valor do tipo adequado, oferecendo um lugar para capturar exceções com base em problemas de recuperação / hidratação da configuração.

A principal desvantagem é que uma mudança estrutural é necessária para cada novo valor; nova coluna de banco de dados, nova coluna no DAL (o mapeamento ou as consultas SQL / SPs), nova coluna de domínio, tudo necessário para testar adequadamente o uso.

A situação adequada para usar qualquer um desses é a situação na qual as desvantagens são mitigadas. Para mim, a maioria das situações de codificação de configuração exigiu uma implementação de linha única. Isso ocorre principalmente porque se você está introduzindo um valor de configuração totalmente novo que governa o comportamento de alguma parte do seu programa, você já precisa alterar o código para usar o novo valor de configuração; por que não aparecer no objeto de configuração e adicionar o valor a ser usado?

Em resumo, um esquema EAV para armazenar configuração realmente não resolve o problema que pretende resolver, e a maioria das soluções alternativas para os problemas apresentados viola o DRY.


3

Especificamente para valores de configuração, eu diria - vá com a única linha. A menos que você esteja passando pelo desenvolvimento, com que frequência essas colunas serão alteradas?

Provavelmente, é melhor proteger o tipo de dados dos valores , em vez de codificar a extensibilidade que você provavelmente não terá no tempo de inatividade entre liberações grandes. Além disso, adicionar ou remover uma única coluna é a migração mais fácil que existe. Não prevejo dor de cabeça ao criar uma nova opção de configuração.

Além disso, você disse que "usuários" podem configurar essas opções sem dar um limite. São configurações por usuário? Nesse caso, argumentarei ainda mais fortemente que as opções de configuração devem estar nas colunas - uma única linha por usuário. Isso poupará muitas dores de cabeça de manutenção posteriormente.


2

Se seus clientes puderem processar fragmentos JSON (que não são apenas matrizes e dicionários, mas também cadeias simples, números, booleanos, valores nulos), será possível ter uma tabela de várias linhas com o nome da opção e um valor de cadeia contendo JSON. Isso permite que você também armazene valores estruturados, e o código para processá-los já deve estar lá.

Se seus clientes não puderem processar fragmentos JSON, obtenha novos clientes.


1

Única linha Prós: bem definido. Contras: Alterar a configuração pode ser uma dor. Migrações de banco de dados etc.

Profissionais de valor da entidade: Super flexível, suporta a evolução da sua configuração. Contras: Integridade referencial? Mais verificações em seu código para verificar se a propriedade existe antes que você possa fazer alguma coisa.

Eu usaria a abordagem 2 apoiada por um banco de dados não relacional como o Mongo. Se há algo que você pode ter certeza, sua mudança.


1

Use ambos!

Classifique quais opções podem ter várias instâncias e quais são genéricas.

A tabela de linha única (configurações)

  id  |  company_name  |  start_fullscreen  |  refresh_seconds  |  ...
------+----------------+--------------------+-------------------+-------
  4   |  ACME Inc.     |  true              |  20               |  ...

A tabela nome-valor-par (opções)

  name             |  value          | update_time  
-------------------+-----------------+--------------
  generic_option_1 |  Option 1 Value | timestamp    
  generic_option_2 |  Option 2 Value | timestamp    
  generic_option_3 |  Option 3 Value | timestamp    
  configuration    |  4              | timestamp    
  ...              |  ...            | ...          

Eu acho que isso é mais flexível.

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.