2008-09-23 12 views
7

Este es un escenario que he visto en varios lugares a lo largo de los años; Me pregunto si alguien más ha encontrado una mejor solución que yo ...Diseñar un esquema de 'Orden' en el que hay tablas de definición de producto dispares

Mi empresa vende un número relativamente pequeño de productos, sin embargo, los productos que vendemos son altamente especializados (es decir, para seleccionar un producto determinado) , se debe proporcionar una cantidad significativa de detalles al respecto). El problema es que mientras el cantidad de los detalles requeridos para elegir un producto determinado es relativamente constante, los tipos de los detalles necesarios varían mucho entre los productos. Por ejemplo:

Producto X podría tener la identificación de características similares (hipotéticamente)

  • 'Color',
  • 'material'
  • 'tiempo medio entre fallos'

pero Producto Y podría tener características

  • 'Espesor',
  • 'Diámetro'
  • 'Fuente de energía'

El problema (uno de ellos, de todos modos) en la creación de un sistema de orden que utiliza tanto los productos X e Y es que una Orden Line debe referirse, en algún momento, a lo que está "vendiendo". Dado que el Producto X y el Producto Y se definen en dos tablas diferentes, y la desnormalización de productos utilizando un esquema de tabla amplia no es una opción (las definiciones del producto son bastante profundas), es difícil ver una manera clara de definir la Línea de pedido en tal forma en que la entrada, edición e informes de órdenes son prácticas.


cosas que he intentado en el pasado

  • Crear una tabla padre llamado 'producto' con columnas comunes a los productos X e Y, a continuación, utilizando 'producto' como referencia para la tabla OrderLine, y la creación de una relación FK con 'Producto' como el lado primario entre las tablas para el Producto X y el Producto Y. Esto básicamente coloca la tabla 'Producto' como la matriz de OrderLine y de todas las tablas de producto dispares (por ejemplo, Productos X y Y). Funciona bien para la entrada de pedidos, pero causa problemas con el informe o la edición de pedidos ya que el registro 'Producto' debe rastrear qué tipo de producto es para determinar cómo unir 'Producto' a su hijo más detallado, Producto X o Producto Y. Ventajas: se mantienen las relaciones clave. Desventajas: informes, edición en el nivel de línea/producto de pedido.
  • Cree las columnas 'Tipo de producto' y 'Clave del producto' en el nivel de la Línea de pedido, luego use algunas lógicas o vistas CASE para determinar el producto personalizado al que hace referencia la línea. Esto es similar al artículo (1), sin la tabla común de 'Producto'. Considero que es una solución más "rápida y sucia", ya que elimina por completo las claves externas entre las líneas de pedido y sus definiciones de productos. Ventajas: solución rápida. Desventajas: igual que el artículo (1), más pérdida de RI.
  • Homogeneice las definiciones de producto creando una tabla de encabezado común y utilizando pares clave/valor para los atributos personalizados (OrderLine [n] < - [1] Producto [1] < - [n] ProductAttribute). Ventajas: se mantienen las relaciones clave; sin ambigüedad sobre la definición del producto. Desventajas : informes (recuperar una lista de productos con sus atributos, por ejemplo), los datos de la tipificación de valores de atributos, (ir a buscar los atributos del producto, insertar o actualizar los atributos del producto, etc.) el rendimiento

si alguien más ha intentado una estrategia diferente con más éxito, me gustaría saber de eso.

Gracias.

Respuesta

2

Esto podría comenzar. Necesitará algún refinamiento

Table Product (id PK, name, price, units_per_package) 
Table Product_Attribs (id FK ref Product, AttribName, AttribValue) 

Lo que le permitiría adjuntar una lista de atributos a los productos. - Esta es esencialmente la opción 3

Si conoces a un número máximo de atributos, usted podría ir

Table Product (id PK, name, price, units_per_package, attrName_1, attrValue_1 ...) 

Lo cual, por supuesto, de-normalizar la base de datos, pero más fácil realizar consultas.

Yo prefiero la primera opción porque

  1. Es compatible con un número arbitrario de atributos.
  2. Los nombres de atributos se pueden almacenar en otra tabla y se puede aplicar la integridad referencial para que esos malditos canadienses no introduzcan un "color" y rompan los informes.
1

¿Su línea de productos cambia alguna vez?
Si lo hace, crear una tabla por producto le costará caro, y la idea de pares clave/valor le servirá. Ese es el tipo de dirección hacia abajo del cual estoy dibujado naturalmente.

me gustaría crear tablas de la siguiente manera:

Attribute(attribute_id, description, is_listed)  
-- contains values like "colour", "width", "power source", etc. 
-- "is_listed" tells us if we can get a list of valid values: 

AttributeValue(attribute_id, value) 
-- lists of valid values for different attributes. 

Product (product_id, description) 

ProductAttribute (product_id, attribute_id) 
-- tells us which attributes apply to which products 

Order (order_id, etc) 

OrderLine (order_id, order_line_id, product_id) 

OrderLineProductAttributeValue (order_line_id, attribute_id, value) 
-- tells us things like: order line 999 has "colour" of "blue" 

El SQL para tirar de esto juntos no es trivial, pero no es demasiado complejo, ya sea ... y la mayor parte se escribe una vez y mantener (ya sea en procedimientos almacenados o su capa de acceso a datos).

Hacemos cosas similares con una serie de tipos de entidades.

0

Chris y AJ: gracias por sus respuestas. La línea de productos puede cambiar, pero yo no lo llamaría "volátil".

La razón por la que no me gusta la tercera opción es porque se aplica a los metadatos de los valores de los atributos del producto. Básicamente convierte las columnas en filas, perdiendo la mayoría de las ventajas de la columna de la base de datos en el proceso (tipo de datos, valor predeterminado, restricciones, relaciones con claves foráneas, etc.)

He estado involucrado en un proyecto anterior en el que la definición del producto se hizo de esta manera. Fundamentalmente, creamos un sistema completo de definición de atributos de producto/producto (tipos de datos, ocurrencias mín./Máx., Valores predeterminados, indicadores "requeridos", escenarios de uso, etc.El sistema funcionó, en última instancia, pero tuvo un costo significativo en gastos generales y rendimiento (por ejemplo, vistas materializadas para visualizar productos, componentes "inteligentes" personalizados para representar y validar la IU de entrada de datos para la definición del producto, otro componente "inteligente" para representar el producto los atributos personalizables de la instancia en la línea de orden, blahblahblah).

Nuevamente, gracias por sus respuestas!

5

La primera solución que describe es la mejor si desea mantener la integridad de los datos, y si tiene relativamente pocos tipos de productos y rara vez agrega nuevos tipos de productos. Este es el diseño que elegiría en su situación. Los informes son complejos solo si sus informes necesitan los atributos específicos del producto. Si sus informes solo necesitan los atributos en la tabla de Productos comunes, está bien.

La segunda solución que describes se llama "Asociaciones polimórficas" y no sirve. Su "clave externa" no es una clave externa real, por lo que no puede usar una restricción de DRI para garantizar la integridad de los datos. El polimorfismo OO no tiene un análogo en el modelo relacional.

La tercera solución que describes, que implica almacenar un nombre de atributo como una cadena, es un diseño llamado "Entidad-Valor-Atributo" y puedes ver que esta es una solución costosa y dolorosa. No hay forma de garantizar la integridad de los datos, no hay forma de hacer que un atributo NO sea NULO, no hay forma de asegurarse de que un determinado producto tenga un determinado conjunto de atributos. No hay manera de restringir un atributo a una tabla de búsqueda. Muchos tipos de consultas agregadas se vuelven imposibles de hacer en SQL, por lo que debe escribir muchos códigos de aplicación para hacer informes. Utilice el diseño de EAV solo si debe, por ejemplo, si tiene un número ilimitado de tipos de productos, la lista de atributos puede ser diferente en cada fila, y su esquema debe acomodar nuevos tipos de productos frecuentemente, sin cambios de código o esquema.

Otra solución es "Herencia de tabla única". Utiliza una tabla extremadamente amplia con una columna para cada atributo de cada producto. Deje NULLs en columnas que son irrelevantes para el producto en una fila determinada. Esto significa que no puede declarar un atributo como NOT NULL (a menos que esté en el grupo común a todos los productos). Además, la mayoría de los productos RDBMS tienen un límite en el número de columnas en una sola tabla, o el ancho total en bytes de una fila. Por lo tanto, tiene una cantidad limitada de tipos de productos que puede representar de esta manera.

Existen soluciones híbridas, por ejemplo, puede almacenar atributos comunes normalmente, en columnas, pero atributos específicos del producto en una tabla Entity-Attribute-Value. O bien, puede almacenar atributos específicos del producto de alguna otra forma estructurada, como XML o YAML, en una columna BLOB de la tabla Productos. Pero estas soluciones híbridas sufren porque ahora se deben buscar algunos atributos de manera diferente

La solución definitiva para situaciones como esta es utilizar un modelo de datos semánticos, utilizando RDF en lugar de una base de datos relacional. Esto comparte algunas características con EAV pero es mucho más ambicioso. Todos los metadatos se almacenan de la misma manera que los datos, por lo que cada objeto es autodescriptivo y puede consultar la lista de atributos para un producto determinado tal como lo haría con los datos. Existen productos especiales, como Jena o Sesame, implementando este modelo de datos y un lenguaje de consulta especial que es diferente de SQL.

2

No hay una bala mágica que hayas pasado por alto.

Tiene lo que a veces se llaman "subclases disjuntas". Está la superclase (Producto) con dos subclases (ProductoX) y (ProductoY). Este es un problema que, para las bases de datos relacionales, es realmente difícil. [Otro problema difícil es la Lista de materiales. Otro problema difícil es Gráficos de nodos y arcos.]

Realmente desea el polimorfismo, donde OrderLine está vinculado a una subclase de Producto, pero no sabe (ni le importa) qué subclase específica.

No tiene muchas opciones para modelar. Has identificado bastante las malas características de cada uno. Este es prácticamente todo el universo de elecciones.

  1. Empuja todo hasta la superclase. Ese es el enfoque uni-table donde tiene Producto con un discriminador (tipo = "X" y tipo = "Y") y un millón de columnas. Las columnas de Producto son la unión de columnas en ProductX y ProductY. Habrá nulos por todas partes debido a las columnas no utilizadas.

  2. Empuje todo hacia abajo en las subclases. En este caso, necesitará una vista que sea la unión de ProductX y ProductY. Esa vista es lo que se unió para crear un orden completo. Esta es como la primera solución, excepto que está construida dinámicamente y no se optimiza bien.

  3. Unirse a la instancia de Superclass a la instancia de la subclase. En este caso, la tabla Producto es la intersección de las columnas ProductX y ProductY. Cada producto tiene una referencia a una clave en ProductX o ProductY.

No hay realmente una nueva dirección audaz. En la vista mundial de la base de datos relacional, esas son las opciones.

Si, sin embargo, elige cambiar la forma en que crea el software de la aplicación, puede salir de esta trampa. Si la aplicación está orientada a objetos, puede hacer todo con objetos polimórficos de primera clase. Tienes que mapear desde el procesamiento relacional tipo de torpe; esto ocurre dos veces: una vez cuando recuperas cosas de la base de datos para crear objetos y una vez cuando persistes objetos de vuelta a la base de datos.

La ventaja es que puede describir su procesamiento de manera concisa y correcta. Como objetos, con relaciones de subclase.

La desventaja es que su SQL depende de las incorporaciones masivas simplistas, actualizaciones e inserciones.

Esto se convierte en una ventaja cuando el SQL está aislado en una capa ORM y se gestiona como un tipo de detalle de implementación trivial. Los programadores de Java usan iBatis (o Hibernate o TopLink o Cocoon), los programadores de Python usan SQLAlchemy o SQLObject. El ORM hace que la base de datos recupere y guarde; su aplicación manipula directamente Órdenes, Líneas y Productos.

Cuestiones relacionadas