Cómo diseñar una tabla de productos para muchos tipos de productos donde cada producto tiene muchos parámetros

140

No tengo mucha experiencia en el diseño de mesas. Mi objetivo es crear una o más tablas de productos que cumplan los siguientes requisitos:

  • Admite muchos tipos de productos (TV, teléfono, PC, ...). Cada tipo de producto tiene un conjunto diferente de parámetros, como:

    • El teléfono tendrá color, tamaño, peso, sistema operativo ...

    • La PC tendrá CPU, HDD, RAM ...

  • El conjunto de parámetros debe ser dinámico. Puede agregar o editar cualquier parámetro que desee.

¿Cómo puedo cumplir estos requisitos sin una tabla separada para cada tipo de producto?

Corazón de piedra
fuente

Respuestas:

233

Tiene al menos estas cinco opciones para modelar la jerarquía de tipos que describe:

  • Herencia de tabla única : una tabla para todos los tipos de productos, con suficientes columnas para almacenar todos los atributos de todos los tipos. Esto significa muchas columnas, la mayoría de las cuales son NULL en cualquier fila dada.

  • Herencia de tabla de clase : una tabla para productos, que almacena atributos comunes a todos los tipos de productos. Luego, una tabla por tipo de producto, que almacena atributos específicos para ese tipo de producto.

  • Herencia de tablas de concreto : no hay tabla para los atributos comunes de los productos. En cambio, una tabla por tipo de producto, que almacena los atributos comunes del producto y los atributos específicos del producto.

  • LOB serializado : una tabla para productos, que almacena atributos comunes a todos los tipos de productos. Una columna adicional almacena un BLOB de datos semiestructurados, en XML, YAML, JSON u otro formato. Este BLOB le permite almacenar los atributos específicos de cada tipo de producto. Puede usar patrones de diseño elegantes para describir esto, como Fachada y Memento. Pero independientemente de que tenga un conjunto de atributos que no se pueden consultar fácilmente en SQL; tienes que recuperar todo el blob de vuelta a la aplicación y ordenarlo por ahí.

  • Entity-Attribute-Value : una tabla para productos y una tabla que pivota los atributos en filas, en lugar de columnas. EAV no es un diseño válido con respecto al paradigma relacional, pero muchas personas lo usan de todos modos. Este es el "Patrón de propiedades" mencionado por otra respuesta. Vea otras preguntas con la etiqueta eav en StackOverflow para algunos de los escollos.

He escrito más sobre esto en una presentación, Modelado de datos extensibles .


Pensamientos adicionales sobre EAV: aunque muchas personas parecen estar a favor de EAV, yo no. Parece la solución más flexible y, por lo tanto, la mejor. Sin embargo, tenga en cuenta el adagio TANSTAAFL . Estas son algunas de las desventajas de EAV:

  • No hay forma de hacer que una columna sea obligatoria (equivalente a NOT NULL).
  • No hay forma de usar tipos de datos SQL para validar entradas.
  • No hay forma de garantizar que los nombres de los atributos se deletreen de manera consistente.
  • No hay forma de poner una clave externa en los valores de cualquier atributo dado, por ejemplo, para una tabla de búsqueda.
  • Obtener resultados en un diseño tabular convencional es complejo y costoso, porque para obtener atributos de varias filas debe hacer JOINpara cada atributo.

El grado de flexibilidad que EAV le brinda requiere sacrificios en otras áreas, probablemente haciendo que su código sea tan complejo (o peor) de lo que hubiera sido resolver el problema original de una manera más convencional.

Y en la mayoría de los casos, no es necesario tener ese grado de flexibilidad. En la pregunta del OP sobre los tipos de productos, es mucho más simple crear una tabla por tipo de producto para los atributos específicos del producto, por lo que se aplica una estructura consistente al menos para las entradas del mismo tipo de producto.

Usaría EAV solo si se debe permitir que cada fila tenga potencialmente un conjunto distinto de atributos. Cuando tienes un conjunto finito de tipos de productos, EAV es excesivo. La herencia de la tabla de clase sería mi primera opción.


Actualización 2019: cuanto más veo a las personas que usan JSON como una solución para el problema de "muchos atributos personalizados", menos me gusta esa solución. Hace que las consultas sean demasiado complejas, incluso cuando se utilizan funciones especiales de JSON para admitirlas. Se necesita mucho más espacio de almacenamiento para almacenar documentos JSON, en comparación con el almacenamiento en filas y columnas normales.

Básicamente, ninguna de estas soluciones es fácil o eficiente en una base de datos relacional. Toda la idea de tener "atributos variables" está fundamentalmente en desacuerdo con la teoría relacional.

Todo se reduce a que tiene que elegir una de las soluciones en función de cuál es la menos mala para su aplicación. Por lo tanto, necesita saber cómo va a consultar los datos antes de elegir un diseño de base de datos. No hay forma de elegir una solución que sea "mejor" porque cualquiera de las soluciones podría ser la mejor para una aplicación determinada.

Bill Karwin
fuente
11
@HimalayaGarg La opción "4.5" realmente es lo opuesto al punto completo de la publicación de Bill.
user3308043
2
A diferencia de MySQL, SQL Server tiene un amplio soporte para XML, XPath y XQuery. Entonces, para los usuarios de SQL Server, la mejor opción sería almacenar atributos adicionales en una columna de tipo XML (opción 4). De esta manera, NO tiene que "recuperar todo el blob de vuelta a la aplicación y ordenarlo". Incluso puede crear índices en columnas XML en SQL Server.
Delphi.Boy
2
Prefiero Serialized LOB para mi caso. ¿Pero es adecuado para ORM? Yo uso EF.
Mahmood Jenami
@ user2741577, claro, pero probablemente tendrá que escribir un código personalizado para desempaquetar los campos de datos no estructurados del LOB y aplicarlos a cada campo de entidad de su objeto ORM. No sé EF, pero supongo que podrías crear una clase ORM base que haga esto. Debe realizar un seguimiento de qué campos provienen de campos concretos de la fila de la base de datos y qué campos provienen de los campos del LOB, para que pueda volver a formar un LOB cuando sea el momento de guardar el objeto.
Bill Karwin
12

@Corazón de piedra

Iría aquí con EAV y MVC hasta el final.

@Bill Karvin

Estas son algunas de las desventajas de EAV:

  • No hay forma de hacer que una columna sea obligatoria (equivalente a NOT NULL).
  • No hay forma de usar tipos de datos SQL para validar entradas.
  • No hay forma de garantizar que los nombres de los atributos se deletreen de manera consistente.
  • No hay forma de poner una clave externa en los valores de cualquier atributo dado, por ejemplo, para una tabla de búsqueda.

Todas esas cosas que has mencionado aquí:

  • validación de datos
  • nombres de atributos de validación ortográfica
  • columnas / campos obligatorios
  • manejar la destrucción de atributos dependientes

en mi opinión, no pertenezco a una base de datos porque ninguna de las bases de datos es capaz de manejar esas interacciones y requisitos en un nivel adecuado como lo hace el lenguaje de programación de una aplicación.

En mi opinión, usar una base de datos de esta manera es como usar una roca para clavar un clavo. Puedes hacerlo con una roca, pero ¿no se supone que debes usar un martillo que sea más preciso y específicamente diseñado para este tipo de actividad?

Obtener resultados en un diseño tabular convencional es complejo y costoso, porque para obtener atributos de varias filas debe unir para cada atributo.

Este problema se puede resolver haciendo pocas consultas sobre datos parciales y procesándolos en un diseño tabular con su aplicación. Incluso si tiene 600 GB de datos del producto, puede procesarlos en lotes si necesita datos de cada fila de esta tabla.

Ir más allá Si desea mejorar el rendimiento de las consultas, puede seleccionar ciertas operaciones como, por ejemplo, informes o búsqueda de texto global y preparar para ellas tablas de índice que almacenarían los datos requeridos y se regenerarían periódicamente, digamos cada 30 minutos.

Ni siquiera necesita preocuparse por el costo del almacenamiento adicional de datos porque se vuelve cada vez más barato cada día.

Si todavía le preocupa el rendimiento de las operaciones realizadas por la aplicación, siempre puede usar Erlang, C ++, Go Language para preprocesar los datos y luego simplemente procesar los datos optimizados en su aplicación principal.

Pawel Barcik
fuente
you can always use Erlang, C++, Go Language to pre-process the data¿Qué querías decir? En lugar de DB, use Go lang? ¿Podría por favor explicar eso?
Verde
1
Estoy totalmente de acuerdo. EAV es un camino a seguir, especialmente si necesita un nivel de flexibilidad que le permita agregar nuevos tipos de productos y parámetros sin cambios en el esquema db, me refiero a vivir en producción a través de su aplicación. He estado allí, hecho eso. Trabajó para mi. Acerca de las consultas lentas ... ¿alguien ha oído hablar de cachés? ;)
pawel.kalisz
@Green He editado el último párrafo para que quede más claro, pero se trata de pasar sus datos brutos de EAV a un proceso en un lenguaje que pueda manejar transformaciones de datos, búsquedas en una estructura de árbol o cualquier mapa básico para reducir las operaciones realmente rápido y de manera eficiente en memoria. Los detalles aquí dependerían de lo que necesita ser optimizado
Pawel Barcik
6

Si uso el Class Table Inheritancesignificado:

una tabla para Productos, que almacena atributos comunes a todos los tipos de productos. Luego, una tabla por tipo de producto, que almacena atributos específicos para ese tipo de producto. -Bill Karwin

Lo que me gusta más de las sugerencias de Bill Karwin. Puedo prever un inconveniente, que trataré de explicar cómo evitar que se convierta en un problema.

¿Qué plan de contingencia debo tener en marcha cuando un atributo que solo es común a 1 tipo, luego se vuelve común a 2, luego a 3, etc.?

Por ejemplo: (este es solo un ejemplo, no es mi problema real)

Si vendemos muebles, podríamos vender sillas, lámparas, sofás, televisores, etc. El tipo de televisor podría ser el único que tenemos que tiene un consumo de energía. Entonces pondría el power_consumptionatributo en el tv_type_table. Pero luego comenzamos a llevar sistemas de cine en casa que también tienen una power_consumptionpropiedad. OK, es solo otro producto, así que también agregaré este campo stereo_type_table, ya que probablemente sea más fácil en este momento. Pero con el tiempo a medida que comenzamos a transportar cada vez más productos electrónicos, nos damos cuenta de que power_consumptiones lo suficientemente amplio como para que esté en el main_product_table. ¿Qué debería hacer ahora?

Agregue el campo a la main_product_table. Escriba un script para recorrer la electrónica y coloque el valor correcto de cada uno type_tableen main_product_table. Luego suelte esa columna de cada uno type_table.

Ahora si siempre estaba usando la misma GetProductDataclase para interactuar con la base de datos para extraer la información del producto; entonces, si algún cambio en el código ahora necesita ser refactorizado, debería ser solo para esa Clase.

JD Isaacks
fuente
3

Puede tener una tabla de productos y una tabla separada de ProductAdditionInfo con 3 columnas: ID de producto, nombre de información adicional, valor de información adicional. Si el color es utilizado por muchos pero no todos los tipos de Productos, podría ser una columna anulable en la tabla Producto, o simplemente ponerlo en ProductAdditionalInfo.

Este enfoque no es una técnica tradicional para una base de datos relacional, pero he visto que se usa mucho en la práctica. Puede ser flexible y tener un buen rendimiento.

Steve Yegge llama a esto el patrón Propiedades y escribió una larga publicación sobre su uso.

RossFabricant
fuente
44
El patrón de propiedades es solo Entity-Attribute-Value con otro nombre. Se usa ampliamente, pero almacenarlo en una base de datos relacional rompe las reglas de normalización.
Bill Karwin el
2
Para ser sincero, cuando leí la descripción de EAV en la respuesta de @Bills no entendí muy bien lo que estaba explicando. Pero cuando dijiste 3 columns: product ID, additional info name, additional info valueque entendía el concepto. Y en realidad he hecho esto antes, y me encontré con problemas. Sin embargo, no recuerdo por el momento cuáles fueron esos problemas.
JD Isaacks
1
@JDIsaacks En este patrón, un problema común es que no sabemos cuántas UNIONES necesitamos para obtener todos los atributos.
Omid