¿Cuál es la mejor práctica para este problema (diferentes propiedades para diferentes categorías)?
-
03-07-2019 - |
Pregunta
Tengo algunos productos que pertenecen a alguna categoría.
Cada categoría puede tener diferentes propiedades.
Por ejemplo,
- categoría automóviles tiene propiedades color , poder, ... Las
- categorías mascotas tienen propiedades peso , edad , ...
El número de categorías es de aproximadamente 10-15. El número de propiedades en cada categoría es 3-15. La cantidad de productos es muy grande.
El requisito principal para esta aplicación es una muy buena búsqueda. Seleccionaremos la categoría e ingresaremos los criterios para cada propiedad en esta categoría.
Tiene que diseñar una base de datos para este escenario. (SQL Server 2005)
Solución
El enfoque de diseño clásico sería (la estrella denota la columna de clave principal):
Product
ProductId*
CategoryId: FK to Category.CategroyId
Name
Category
CategoryId*
Name
Property
PropertyId*
Name
Type
CategoryProperty
CategoryId*: FK to Category.CategoryId
PropertyId*: FK to Property.PropertyId
ProductProperty
ProductId*: FK to Product.ProductId
PropertyId*: FK to Property.PropertyId
ValueAsString
Si puede vivir con el hecho de que cada valor de propiedad iría a la base de datos como una cadena y la información de conversión de tipo se almacena en la tabla de Propiedades, este diseño sería suficiente.
La consulta sería algo como esto:
SELECT
Product.ProductId,
Product.Name AS ProductName,
Category.CategoryId,
Category.Name AS CategoryName,
Property.PropertyId,
Property.Name AS PropertyName,
Property.Type AS PropertyType,
ProductProperty.ValueAsString
FROM
Product
INNER JOIN Category ON Category.CategoryId = Product.CategoryId
INENR JOIN CategoryProperty ON CategoryProperty.CategoryId = Category.CategoryId
INNER JOIN Property ON Property.PropertyId = CategoryProperty.PropertyId
INNER JOIN ProductProperty ON ProductProperty.PropertyId = Property.PropertyId
AND ProductProperty.ProductId = Product.ProductId
WHERE
Product.ProductId = 1
Cuantas más condiciones WHERE proporcione (de forma conjunta, por ejemplo, usando AND), más rápida será la consulta. Si ha indexado correctamente sus tablas, eso es.
Tal como está, la solución no es ideal para una situación de indexación de texto completo. Aquí podría ayudar una tabla adicional que almacene todo el texto asociado con un ProductId de una manera más desnormalizada. Esta tabla necesitaría actualizarse a través de activadores que escuchen los cambios en la tabla ProductProperty.
Otros consejos
Si el usuario de la aplicación tiene para seleccionar una categoría antes de que pueda buscar, separaría sus productos en diferentes tablas de base de datos por categoría. Esta solución también está indicada por el hecho de que las categorías mismas tienen muy poco en común. Desglosarlo por categoría también hará que cada búsqueda sea mucho más rápida, ya que no se perderá tiempo buscando en los automóviles cuando su usuario esté buscando una mascota.
Una vez que haya dividido los productos en categorías, debería ser fácil crear las tablas utilizando las propiedades comunes de los productos en cada categoría. La interfaz de usuario de su aplicación debe ser dinámica (estoy pensando en un formulario web), ya que las propiedades que el usuario puede elegir deben cambiar cuando el usuario selecciona una categoría.
Tenga en cuenta que si tiene productos que desea enumerar en varias categorías, esta solución generará datos duplicados en sus tablas. Existe un compromiso entre velocidad y normalización al diseñar una base de datos. Si no tiene productos que se ajusten a múltiples categorías, entonces creo que esta será la solución más rápida (en términos de velocidad de búsqueda).
La mayoría de las personas recomiendan utilizar variaciones del diseño Entity-Attribute-Value (EAV). Este diseño es excesivo para su situación e introduce muchos problemas, por ejemplo:
- No puede definir el tipo de datos para un atributo; puede ingresar " banana " para un atributo entero
- No puede declarar un atributo como obligatorio (es decir, NO NULO en una tabla convencional)
- No puede declarar una restricción de clave externa en un atributo
Si tiene un pequeño número de categorías, es mejor usar la solución A en la respuesta de Bogdan Maxim. Es decir, defina una tabla Productos con atributos comunes a todas las categorías, y una tabla adicional para cada categoría, para almacenar los atributos específicos de la categoría.
Solo EAV es una buena solución si tiene un número infinito de categorías o si potencialmente debe admitir un conjunto diferente de atributos por fila en Productos. Pero entonces no está utilizando una base de datos relacional, ya que EAV viola varias reglas de normalización.
Si realmente necesita tanta flexibilidad, sería mejor almacenar sus datos en XML. De hecho, puede consultar RDF y marcos web semánticos como Sesame .
Es posible que desee considerar un tipo de Entity-Attribute-Value arreglo, donde puede " etiquetar " cada producto con pares de atributos de nombre / valor arbitrarios.
Puedes probar esto. No estoy muy seguro de los detalles reales de su pregunta, tal vez alguien pueda ayudarlo a traducir un poco mejor.
5 mesas. 3 para almacenar los datos, 2 para almacenar las asignaciones entre datos.
tProduct
productID
<other product details>
tCategory
categoryID
<other category details>
tProperty
propertyID
<other property details>
tProductXCategory
productyID
categoryID
tCategoryXProperty
categoryID
propertyID
Sus consultas necesitarán unir los datos usando las tablas de mapeo, pero esto le permitirá tener diferentes relaciones entre categorías, propiedades y productos.
Use procedimientos almacenados o consultas parametrizadas para obtener un mejor rendimiento de sus búsquedas.
Si desea ser flexible en sus categorías y propiedades, debe crear las siguientes tablas:
- producto: ProductID
- categoría: Id. de categoría, Id. de producto
- propiedad: PropertyID, CategoryID
cuando desea compartir una categoría sobre más de un producto, debe crear una tabla de enlaces para la unión n: m:
- productCategoryPointer: ProdCatID, ProductID, CategoryID.
Tendrá que unirse a algunas de sus consultas, pero con los índices correctos, podrá consultar sus datos rápidamente.
Podría intentar algo más orientado a objetos.
1. Definir una tabla base para productos
Products(ProductID, CategoryID, <any other common properties>)
2. Definir una tabla Categorías
Categories(CategoryID, Name, Description, ..)
Desde aquí tienes muchas opciones y casi todas romperán la normalización de tu base de datos.
Solución A.
Será una pesadilla de mantenimiento si necesita agregar nuevos productos
A1. Defina una tabla separada para cada una de las categorías
Cars(CarID, ProductID, ..)
Pets(PetID, ProductID, ..)
A2. Unir las tablas según las relaciones para utilizar los datos
SELECT <fields> FROM Cars INNER JOIN Products ON Cars.ProductID = Products.ProductID
Solución B.
Pesadilla de mantenimiento para diferentes tipos de propiedades (es decir, int, varchar, etc.)
B1. Definir una tabla para Propiedades
CategoryProperty (CPID, Name, Type)
B2. Defina una tabla para contener las asociaciones entre Categorías y Propiedades
PropertyAssociation (CPID, PropertyID)
B12. Defina una tabla para contener las propiedades (Alternativa para B1 y B2)
Properties(CategoryID, PropertyID, Name, Type)
B3. Para cada tipo de propiedad (int, double, varchar, etc.) agregue una tabla de valores
PropertyValueInt(ProductID, CPID, PropertyID, Value)
- para int
PropertyValueString(ProductID, CPID, PropertyID, Value)
- para cadenas
PropertyValueMoney(ProductID, CPID, PropertyID, Value)
- por dinero
B4. Une todas las tablas para recuperar la propiedad deseada.
Al usar este enfoque, no tendrá que administrar todas las propiedades en una tabla separada, sino los tipos de valor de ellas. Básicamente todas las tablas involucradas serán tablas de búsqueda. La desventaja es que, para recuperar cada valor, debe & "; Caso &"; para cada tipo de valor.
Tenga en cuenta estos artículos ( aquí y aquí ) al elegir cualquiera de estos enfoques. Esta publicación del foro también es interesante y de alguna manera relacionada con el tema, incluso aunque se trata de localización.
También puede usar La respuesta de Tomalak y agrega una escritura fuerte si sientes la necesidad.
Recientemente tuve que hacer esto y estoy usando NHibernate donde tengo tres entidades
Opción de categoría de producto OptionCategory
Un producto tiene 1 * Categorías
Un producto tiene 1 * Opción
Una opción tiene 1 OptionCategory
una vez configurado, puede utilizar el almacenamiento en caché de Nhibernate
Saludos