Conselho de design de banco de dados

8

Estou projetando um banco de dados para nossa equipe de vendas usar como uma ferramenta de cotação rápida de tarefas. Gostaria de receber algum feedback sobre um aspecto específico do design.

Uma cotação é basicamente criada selecionando uma lista de 'assemblies' predefinidos, cada um com um preço acordado. Uma visão simplificada do formulário principal se parece com isso:

                                                  +------------ --- ---+
                                                  | Assembly options   |
+------------+------------+----------+------------+---+---+---+ --- +--+
| assembly  | unit cost  | quantity | total cost | 1 | 2 | 3 |     |50|
+------------+------------+----------+------------+---+---+---+ --- +--+
| VSD55      | £10'000    | 2        | £25'500    | 1 | 1 |   |     |  | 
| RDOL2.2    | £2'000     | 1        |  £1'500    |   | 1 |   |     |  | 
| DOL5.0     | £1'000     | 1        |  £1'200    |   |   | 1 |     |  | 
+------------+------------+----------+------------+---+---+---+ --- +--+

O usuário seleciona uma montagem predefinida, insere a quantidade e seleciona quaisquer 'opções' necessárias. Cada montagem possui potencialmente até 50 opções disponíveis. Uma opção também é uma montagem predefinida (submontagem) com seu próprio preço. O 'custo total' para cada linha é calculado como (custo da montagem principal * quantidade) + custo de qualquer opção.

Quando o usuário move o cursor para uma caixa de opção, o nome e o preço dessa opção são divulgados a ele.

Agora é aqui que fica complicado. Cada montagem possui sua própria lista de opções disponíveis. ou seja, a opção 1 para um 'VSD55' representa um subconjunto diferente da opção 1 para um DOL5.0.

Quanto às montagens aqui, estão as tabelas simplificadas que estou usando:

+-----------------+    +------------------------+    +-----------------------------+
| assembly        |    | assembly_option        |    | assembly_option_link        |
+-----------------+    +------------------------+    +-----------------------------+
| assembly_id (PK)|    | assembly_option_id (PK)|    | assembly_option_link_id (PK)|
| assembly_name   |    | assembly_option_name   |    | assembly_id (FK)            |
| unit_cost       |    | option_number          |    | assembly_option_id (FK)     |
+-----------------+    | unit_cost              |    +-----------------------------+
                       +------------------------+

A tabela 'assembly_option_link' basicamente define quais opções estão disponíveis para cada montagem.

Agora, para as tabelas 'quote':

 +-----------------+    +------------------------+    
 | quote           |    | quote_assembly         |    
 +-----------------+    +------------------------+    
 | quote_id (PK)   |    | quote_assembly_id (PK) |
 | quote_name      |    | assembly_id (FK)       |
 +-----------------+    | quantity               |
                        +------------------------+    

Agora, a parte complicada é como armazenar as opções selecionadas. Devo expandir a tabela 'quote_assembly' com todos os 50 campos de opção, mesmo que isso quebre as regras de normalização. Uma montagem nunca será selecionada com todas as 50 opções, portanto isso também parece muito ineficiente. No lado positivo, esta solução permite que o formulário de entrada do usuário seja mapeado diretamente para a tabela, facilitando a codificação.

A solução 'normalizada' eu acho que seria criar outra tabela como esta:

+------------------------------+
| quote_assembly_option        |
+------------------------------+
| quote_assembly_option_id (PK)|
| quote_assembly_id (FK)       |
| assembly_option_id (FK)      |
| quantity                     |
+------------------------------+

Esta solução significa que apenas as opções selecionadas são armazenadas. Além disso, em vez de armazenar o número da opção, posso armazenar o 'assembly_option_id' real. Isso torna o cálculo do custo total da cotação mais simples, pois não preciso converter entre 'option_number' e 'assembly_option_id' para procurar o custo da opção de montagem. A principal desvantagem desta solução é que ela não se encaixa bem no formulário de entrada do usuário. Acho que vou precisar aplicar alguma codificação sofisticada para fazer a interface do formulário com as tabelas.

Alguém pode oferecer algum conselho de design aqui, por favor? Espero ter me explicado bem o suficiente.

MAIS INFORMAÇÕES
Também é um relatório de cotação detalhado que expande todas as opções selecionadas como itens de linha separados na montagem principal. Por exemplo:

+---------------------------------+------------+----------+------------+
| assembly                        | unit cost  | quantity | total cost |
+---------------------------------+------------+----------+------------+
| VSD55                           | £10'000    | 2        |   £20'000  |
|   - Seal leak protection        | £ 5'000    | 1        |   £ 5'000  |   <-option 1
|   - Motor over temp protection  | £   500    | 1        |   £   500  |   <-option 2
+---------------------------------+------------+----------+------------+
|                                 |            |          |   £25'500  |
+---------------------------------+------------+----------+------------+
David
fonte

Respostas:

3
                                                  +------------ --- ---+
                                                  | Assembly options   |
+------------+------------+----------+------------+---+---+---+ --- +--+
| assembly  | unit cost  | quantity | total cost | 1 | 2 | 3 |     |50|
+------------+------------+----------+------------+---+---+---+ --- +--+
| VSD55      | £10'000    | 2        | £20'000    | 1 | 1 |   |     |  | 

Se alguém me entregasse essa citação, minha primeira pergunta seria "O que é a opção 1 para o VSD55?" A resposta seria "eu não sei". Essa informação não está na cotação. No caso improvável de a pessoa responder a uma segunda pergunta, essa pergunta seria "Quanto custa?" Mais uma vez, a resposta seria "não sei". Um silêncio muito perturbador se seguiria imediatamente, durante o qual a pessoa que me entregou a citação imaginaria o quanto seria melhor ser atropelado por um trem.

As opções devem ser itens de linha na cotação, juntamente com seu preço unitário, quantidade e preço total. As opções devem ser nomeadas, não numeradas. Eles devem aparecer diretamente sob a assembléia dos pais, também, não espalhados por todo o inferno e metade da Geórgia.

Se você quer uma chance do meu dinheiro, é melhor deixar bem claro o que devo receber pelo meu dinheiro.

Não há nada (muito) errado com 50 caixas de seleção em um formulário da interface do usuário. Isso facilita a escolha de opções. Mas o código da interface do usuário deve ler as caixas de seleção e inserir as informações corretas nas tabelas normalizadas.

Mike Sherrill 'Recolha de Gatos'
fonte
Parece que você está oferecendo conselhos sobre os processos de negócios, que não estão disponíveis para opção, que David está tentando encapsular da melhor maneira que sabe para o projeto ao qual foi designado. ~ Agora, concordo que um dba deve influenciar o design onde for possível, mas às vezes isso não pode ser ajudado. Além disso, mantenha em mente que esta é uma ferramenta interna (ver a primeira linha)
jcolebrand
11
Comentários bastante justos, me fizeram rir! Naturalmente, temos um relatório de cotação que faz exatamente o que você está sugerindo. Desde então, acrescentei esse detalhe à pergunta original para evitar mais discussões fora do tópico;) #
David David
3

A última opção que você dá é a maneira como eu iria. E retorne duas tabelas agrupadas, uma para a "linha principal" e outra para as linhas "existentes" coletadas para as 50 colunas. Supondo que você possa mapear a opção para seu ID de coluna apropriado com bastante facilidade (parece que você pode, sem muita dificuldade).

Isso seria fácil o suficiente para a iteração, assumindo uma linguagem como C #, onde você tem o LINQ disponível, etc. Isso é fácil de fazer, mesmo que envolva um pouco de loop (é o código da interface do usuário, isso precisa ser feito em algum momento). ) Ou você pode fazer um pivô no banco de dados antes de retornar ... isso seria mais rápido. Mas ainda manterá a complexidade.

Mas o seu design soa para mim.

jcolebrand
fonte
0

A adição de sua mesa extra também parece bastante boa para mim.

Ao lidar com um problema semelhante do meu lado, explorei o armazenamento de uma árvore na tabela order_lines. Caso você considerasse algo semelhante, eu tinha:

  • um parent_idcampo extra para order_linese uma chave estrangeira para que (parent_id, product_id)referencie(order_line_id, product_id)

  • Uma restrição de verificação para tornar possível ter uma opção implicaria um pai (no meu caso check((option_id is not null) = (parent_id is not null))).

Em outras palavras, deixo que a interface do usuário tenha uma palavra sobre como as coisas devem ser armazenadas:

+---------------------------------+------------+----------+------------+
| assembly                        | unit cost  | quantity | total cost |
+---------------------------------+------------+----------+------------+
| VSD55                           | £10'000    | 2        |   £20'000  |
|   - Seal leak protection        | £ 5'000    | 1        |   £ 5'000  |
|   - Motor over temp protection  | £   500    | 1        |   £   500  |
+---------------------------------+------------+----------+------------+

Do ponto de vista da codificação da interface do usuário, parecia certo. Mas rapidamente se sentiu errado do ponto de vista das regras de negócios, na medida em que introduziu uma variedade de questões no caminho. (Eu tive que lidar com todos os tipos de casos especiais em gatilhos.)

Portanto, não recomendado ... Na medida em que eu já tive casos semelhantes, sua abordagem atual será menos propensa a problemas no futuro.

Denis de Bernardy
fonte