2008-12-16 13 views
14

Estoy tratando de algo así como Database Design for Tagging, excepto que cada una de mis etiquetas se agrupan en categorías.¿Cómo diseñar un esquema de base de datos para admitir el etiquetado con categorías?

Por ejemplo, supongamos que tengo una base de datos sobre vehículos. Digamos que en realidad no sabemos mucho sobre vehículos, por lo que no podemos especificar las columnas que tendrán todos los vehículos. Por lo tanto, "etiquetaremos" vehículos con información.

1. manufacture: Mercedes 
    model: SLK32 AMG 
    convertible: hardtop 

2. manufacture: Ford 
    model: GT90 
    production phase: prototype 

3. manufacture: Mazda 
    model: MX-5 
    convertible: softtop 

Ahora bien, como se puede ver todos los coches están etiquetadas con su fabricación y modelo, pero las otras categorías no hacer todo el partido. Tenga en cuenta que un automóvil solo puede tener uno de cada categoría. ES DECIR. Un automóvil solo puede tener un fabricante.

Quiero diseñar una base de datos para apoyar una búsqueda de todos los Mercedes, o para poder enumerar todos los fabricantes.

mi diseño actual es algo como esto:

vehicles 
    int vid 
    String vin 

vehicleTags 
    int vid 
    int tid 

tags 
    int tid 
    String tag 
    int cid 

categories 
    int cid 
    String category 

tengo todas las claves primarias y externas adecuadas en el lugar, excepto que no puedo manejar el caso en el que cada vehículo sólo puede tener un solo fabricante. ¿O puedo?

¿Puedo agregar una restricción de clave externa a la clave primaria compuesta en etiquetas de vehículo? ES DECIR. ¿Podría agregar una restricción tal que la clave primaria compuesta (vid, tid) solo se pueda agregar a los Títulos de vehículo solo si no hay una fila en los Títulos de vehículo, de modo que para el mismo video, todavía no hay un tid en el el mismo cid?

Supongo que no. Creo que la solución a este problema es agregar una columna cid a vehicleTags, y crear la nueva clave primaria compuesta (vid, cid). Que se vería así:

vehicleTags 
    int vid 
    int cid 
    int tid 

Esto evitaría tener un automóvil de dos fabricantes, pero ahora han duplicado la información que se encuentra en tid cid.

¿Cuál debería ser mi esquema?

Tom se dio cuenta de este problema en mi esquema de base de datos en una pregunta anterior, How do you do many to many table outer joins?

EDITAR
Sé que en el ejemplo de fabricación debe ser realmente una columna en la tabla de vehículo, pero digamos que usted puede' hacer eso El ejemplo es solo un ejemplo.

+0

Creo que la respuesta correcta ahora es utilizar una base de datos nosql. Quién necesita un esquema – Pyrolistical

Respuesta

12

Esta es otra variación del diseño Entity-Attribute-Value.

Una tabla EAV más reconocibles tiene el siguiente aspecto:

CREATE TABLE vehicleEAV (
    vid  INTEGER, 
    attr_name VARCHAR(20), 
    attr_value VARCHAR(100), 
    PRIMARY KEY (vid, attr_name), 
    FOREIGN KEY (vid) REFERENCES vehicles (vid) 
); 

Algunas personas forzar attr_name hacer referencia a una tabla de búsqueda de nombres de atributos predefinidos, para limitar el caos.

Lo que has hecho es simplemente difundir una mesa de EAV más de tres mesas, pero sin mejorar el orden de los metadatos:

CREATE TABLE vehicleTag (
    vid   INTEGER, 
    cid   INTEGER, 
    tid   INTEGER, 
    PRIMARY KEY (vid, cid), 
    FOREIGN KEY (vid) REFERENCES vehicles(vid), 
    FOREIGN KEY (cid) REFERENCES categories(cid), 
    FOREIGN KEY (tid) REFERENCES tags(tid) 
); 

CREATE TABLE categories (
    cid  INTEGER PRIMARY KEY, 
    category VARCHAR(20) -- "attr_name" 
); 

CREATE TABLE tags (
    tid  INTEGER PRIMARY KEY, 
    tag  VARCHAR(100) -- "attr_value" 
); 

Si usted va a utilizar el diseño EAV, sólo es necesario el vehicleTags y categories tablas.

CREATE TABLE vehicleTag (
    vid   INTEGER, 
    cid   INTEGER,  -- reference to "attr_name" lookup table 
    tag   VARCHAR(100, -- "attr_value" 
    PRIMARY KEY (vid, cid), 
    FOREIGN KEY (vid) REFERENCES vehicles(vid), 
    FOREIGN KEY (cid) REFERENCES categories(cid) 
); 

Pero hay que tener en cuenta que estás datos de mezcla con metadatos. Pierdes la habilidad de aplicar ciertas restricciones a tu modelo de datos.

  • ¿Cómo se puede hacer que una de las categorías sea obligatoria (una columna convencional utiliza una restricción NOT NULL)?
  • ¿Cómo se pueden usar los tipos de datos SQL para validar algunos de los valores de las etiquetas? No puedes, porque estás usando una cadena larga para cada valor de etiqueta. ¿Esta cadena es lo suficientemente larga para cada etiqueta que necesitarás en el futuro? No puedes decir.
  • ¿Cómo puede restringir algunas de sus etiquetas a un conjunto de valores permitidos (una tabla convencional utiliza una clave externa para una tabla de búsqueda)? Este es su ejemplo "softtop" vs. "soft top". Pero no se puede establecer una restricción en la columna tag porque esa restricción se aplicaría a todos los demás valores de etiquetas para otras categorías. También debe restringir el tamaño del motor y el color de la pintura a la "parte superior blanda".

Las bases de datos SQL no funcionan bien con este modelo. Es extremadamente difícil acertar, y consultarlo se vuelve muy complejo. Si continúa utilizando SQL, será mejor que modele las tablas de manera convencional, con una columna por atributo. Si necesita tener "subtipos", defina una tabla subordinada por subtipo (Class-Table Inheritance) o use Single-Table Inheritance. Si tiene una variación ilimitada en los atributos por entidad, entonces use Serialized LOB.

Otra tecnología que está diseñada para este tipo de modelos de datos fluidos y no relacionales es una base de datos semántica que almacena datos en RDF y se consulta con SPARQL. Una solución gratuita es Sesame.

+0

Es por eso que SO es tan increíble. Siempre sentí que estaba reinventando la rueda, pero antes SO, no tenía una buena manera de preguntar si estaba ... Aunque no respondiste exactamente mi pregunta, definitivamente está en la dirección correcta. Gracias. – Pyrolistical

+0

@Bill Gracias por la explicación y los enlaces a EAV. Estaba teniendo un problema similar y eso realmente me ayudó a entenderlo más. – MD3Sum

0

Creo que su solución es simplemente agregar una columna de fabricante a su tabla de vehículos. Es un atributo que usted sabe que todos los vehículos tendrán (es decir, los automóviles no aparecen espontáneamente por sí mismos) y al convertirlo en una columna en la tabla de su vehículo, usted resuelve el problema de tener un solo fabricante para cada vehículo. Este enfoque se aplicaría a cualquier atributo que sepa que será compartido por todos los vehículos. Luego puede implementar el sistema de etiquetado para los otros atributos que no son universales.

por lo que tomar de su ejemplo la mesa vehículo sería algo así como:

vehicle 
    vid 
    vin 
    make 
    model 
+0

Usted se está enfocando en el ejemplo y no en mi problema. Mi ejemplo no refleja completamente mi problema. – Pyrolistical

0

Una forma sería reconsiderar ligeramente su esquema, la normalización de claves de etiquetas lejos de los valores:

vehicles 
    int vid 
    string vin 

tags 
    int tid 
    int cid 
    string key 

categories 
    int cid 
    string category 

vehicleTags 
    int vid 
    int tid 
    string value 

Ahora todo lo que necesita es una restricción única en vehicleTags(vid, tid).

Alternativamente, hay formas de crear restricciones más allá de las claves externas simples: dependiendo de su base de datos, ¿puede escribir una restricción personalizada o un activador de inserción/actualización para imponer la exclusividad de la etiqueta del vehículo?

+0

¿Qué es "clave de cadena" debajo de las etiquetas? – Pyrolistical

+0

esto no funcionaría. Si quisiera cambiar "softtop" a "soft top", tendría que actualizar todos los valores en las etiquetas del vehículo, ¡y VehicleTags es enorme! – Pyrolistical

+0

@Pyrolistical, cuando dices que una etiqueta es así: fabricación: Mercedes 'fabricación' es la clave de la etiqueta y 'Mercedes' es el valor de la etiqueta. A partir de los datos de muestra que suministró, parece que las partes clave están duplicadas y, por lo tanto, se pueden normalizar en su propia tabla. –

0

Necesitaba resolver este problema exacto (mismo dominio general y todo: partes de automóviles). Descubrí que la mejor solución al problema era usar Lucene/Xapian/Ferret/Sphinx o cualquier indexador de texto completo que prefiriera. Mucho mejor rendimiento que lo que SQL puede ofrecer.

En la actualidad, casi nunca termino construyendo una aplicación web respaldada por una base de datos que no involucre un indexador de texto completo. Este problema y el problema general de la búsqueda surgen con demasiada frecuencia para omitir los indizadores de su caja de herramientas.

3

Necesitaba resolver este problema exacto (el mismo dominio general y todo: partes de automóviles). Descubrí que la mejor solución al problema era usar Lucene/Xapian/Ferret/Sphinx o cualquier indexador de texto completo que prefiriera. Mucho mejor rendimiento que lo que SQL puede ofrecer.

Cuestiones relacionadas