Estoy diseñando una base de datos para que nuestro equipo de ventas la use como una herramienta rápida para cotizar trabajos. Me gustaría recibir comentarios sobre un aspecto particular del diseño.
Básicamente, se crea una cotización seleccionando una lista de 'ensambles' predefinidos, cada uno con un precio acordado. Una vista simplificada del formulario principal se ve así:
+------------ --- ---+
| 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 | | |
+------------+------------+----------+------------+---+---+---+ --- +--+
El usuario selecciona un ensamblaje predefinido, ingresa la cantidad y selecciona cualquier 'opción' requerida. Cada conjunto tiene potencialmente hasta 50 opciones disponibles. Una opción también es un ensamblaje predefinido (subensamblaje) con su propio precio. El 'costo total' para cada línea se calcula como (costo de ensamblaje principal * cantidad) + costo de cualquier opción.
Cuando el usuario mueve su cursor a un cuadro de opción, el nombre y el precio de esa opción se dan a conocer.
Ahora aquí es donde se complica. Cada conjunto tiene su propia lista de opciones disponibles. es decir, la opción 1 para un 'VSD55' representa un subconjunto diferente que la opción 1 para un DOL5.0.
En cuanto a los ensamblajes, aquí están las tablas simplificadas que estoy 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 | +-----------------------------+
+------------------------+
La tabla 'assembly_option_link' básicamente define qué opciones están disponibles para cada ensamblaje.
Ahora para las tablas de 'cotización':
+-----------------+ +------------------------+
| quote | | quote_assembly |
+-----------------+ +------------------------+
| quote_id (PK) | | quote_assembly_id (PK) |
| quote_name | | assembly_id (FK) |
+-----------------+ | quantity |
+------------------------+
Ahora la parte difícil es cómo almacenar las opciones seleccionadas. ¿Debo expandir la tabla 'quote_assembly' con los 50 campos de opciones aunque esto rompa las reglas de normalización? Nunca se seleccionará un ensamblaje con las 50 opciones, por lo que esto también parece muy ineficiente. En el lado positivo, esta solución permite que el formulario de entrada del usuario se asigne directamente a la tabla, lo que facilita la codificación.
La solución 'normalizada' creo que sería crear otra tabla como esta:
+------------------------------+
| quote_assembly_option |
+------------------------------+
| quote_assembly_option_id (PK)|
| quote_assembly_id (FK) |
| assembly_option_id (FK) |
| quantity |
+------------------------------+
Esta solución significa que solo se almacenan las opciones seleccionadas. Además, en lugar de almacenar el número_opción, puedo almacenar el 'ensamblado_opción_id' real. Esto hace que el cálculo del costo total de la cotización sea más simple, ya que no necesito convertir entre 'option_number' y 'assembly_option_id' para buscar el costo de la opción de ensamblaje. Sin embargo, el principal inconveniente de esta solución es que no se adapta bien al formulario de entrada del usuario. Creo que necesitaré aplicar una codificación elegante para interconectar el formulario con las tablas.
¿Alguien puede ofrecer algún consejo de diseño aquí, por favor? Espero haberme explicado lo suficientemente bien.
MÁS INFORMACIÓN
También es un informe de presupuesto detallado que amplía las opciones seleccionadas como líneas de pedido separadas en el ensamblaje principal. Por ejemplo:
+---------------------------------+------------+----------+------------+
| 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 |
+---------------------------------+------------+----------+------------+
La última opción que das es la forma en que yo iría con ella. Y devuelva dos tablas agrupadas, una para la "fila principal" y otra para las filas recopiladas "existe" para las 50 columnas. Suponiendo que puede asignar la opción a su ID de columna adecuada con bastante facilidad (parece que puede hacerlo sin demasiado tiempo difícil).
Eso sería bastante fácil para la iteración, suponiendo un lenguaje como C #, donde tiene LINQ disponible, etc. Esos son lo suficientemente fáciles de hacer, incluso si implican un poco de bucle (es el código de la interfaz de usuario, debe hacerse en algún momento ) O podría hacer un pivote en la base de datos antes de regresar ... eso sería más rápido. Pero aún mantendrá la complejidad.
Pero tu diseño me suena bien.
fuente
Agregar su mesa extra también me parece bastante acertado.
Al tratar con un problema similar de mi parte, exploré el almacenamiento de un árbol en la tabla order_lines. En caso de que haya considerado algo similar, tuve:
un
parent_id
campo adicional paraorder_lines
y una clave foránea para que haga(parent_id, product_id)
referencia(order_line_id, product_id)
Una restricción de verificación para tener una opción implicaría un padre (en mi caso
check((option_id is not null) = (parent_id is not null))
).En otras palabras, dejo que la interfaz de usuario tenga una palabra sobre cómo deben almacenarse las cosas:
Desde el punto de vista de la codificación UI, parecía correcto. Pero rápidamente se sintió mal desde el punto de vista de la regla de negocios, ya que introdujo una variedad de problemas en el futuro. (Tuve que lidiar con todo tipo de casos especiales en los desencadenantes).
Por lo tanto, no se recomienda ... En la medida en que he experimentado casos similares, su enfoque actual será menos propenso a problemas en el futuro.
fuente