Diseño de base de datos SQL recomendado para etiquetas o etiquetado [cerrado]

Resuelto dlamblin asked hace 16 años • 6 respuestas

He oído hablar de algunas formas de implementar el etiquetado; usar una tabla de mapeo entre TagID y ItemID (tiene sentido para mí, pero ¿se escala?), agregar un número fijo de posibles columnas TagID a ItemID (parece una mala idea), mantener etiquetas en una columna de texto separada por comas (suena loco pero podría funcionar). Incluso escuché a alguien recomendar una matriz dispersa, pero entonces, ¿cómo crecen con gracia los nombres de las etiquetas?

¿Me estoy perdiendo una de las mejores prácticas para las etiquetas?

dlamblin avatar Aug 22 '08 02:08 dlamblin
Aceptado

Tres tablas (una para almacenar todos los elementos, una para todas las etiquetas y otra para la relación entre las dos), indexadas correctamente, con claves externas configuradas ejecutándose en una base de datos adecuada, deberían funcionar bien y escalar adecuadamente.

Table: Item
Columns: ItemID, Title, Content

Table: Tag
Columns: TagID, Title

Table: ItemTag
Columns: ItemID, TagID
Yaakov Ellis avatar Aug 21 '2008 19:08 Yaakov Ellis

Normalmente estaría de acuerdo con Yaakov Ellis pero en este caso especial hay otra solución viable:

Utilice dos tablas:

Table: Item
Columns: ItemID, Title, Content
Indexes: ItemID

Table: Tag
Columns: ItemID, Title
Indexes: ItemId, Title

Esto tiene algunas ventajas importantes:

Primero, simplifica mucho el desarrollo: en la solución de tres tablas para insertar y actualizar, itemdebe buscar la Tagtabla para ver si ya hay entradas. Luego hay que unirlos con otros nuevos. Ésta no es una tarea trivial.

Luego simplifica las consultas (y quizás más rápido). Hay tres consultas principales a la base de datos que deberá realizar: generar todo Tagspara uno Item, dibujar una nube de etiquetas y seleccionar todos los elementos para un título de etiqueta.

Todas las etiquetas para un artículo:

3-Tabla:

SELECT Tag.Title 
  FROM Tag 
  JOIN ItemTag ON Tag.TagID = ItemTag.TagID
 WHERE ItemTag.ItemID = :id

2-Tabla:

SELECT Tag.Title
FROM Tag
WHERE Tag.ItemID = :id

Nube de etiquetas:

3-Tabla:

SELECT Tag.Title, count(*)
  FROM Tag
  JOIN ItemTag ON Tag.TagID = ItemTag.TagID
 GROUP BY Tag.Title

2-Tabla:

SELECT Tag.Title, count(*)
  FROM Tag
 GROUP BY Tag.Title

Artículos para una etiqueta:

3-Tabla:

SELECT Item.*
  FROM Item
  JOIN ItemTag ON Item.ItemID = ItemTag.ItemID
  JOIN Tag ON ItemTag.TagID = Tag.TagID
 WHERE Tag.Title = :title

2-Tabla:

SELECT Item.*
  FROM Item
  JOIN Tag ON Item.ItemID = Tag.ItemID
 WHERE Tag.Title = :title

Pero también hay algunos inconvenientes: podría ocupar más espacio en la base de datos (lo que podría generar más operaciones en el disco, lo que es más lento) y no está normalizado, lo que podría generar inconsistencias.

El argumento del tamaño no es tan fuerte porque la naturaleza misma de las etiquetas es que normalmente son bastante pequeñas, por lo que el aumento de tamaño no es grande. Se podría argumentar que la consulta del título de la etiqueta es mucho más rápida en una tabla pequeña que contiene cada etiqueta solo una vez y esto ciertamente es cierto. Pero teniendo en cuenta los ahorros que supone no tener que inscribirse y el hecho de que se puede crear un buen índice sobre ellos, esto podría compensarse fácilmente. Por supuesto, esto depende en gran medida del tamaño de la base de datos que esté utilizando.

El argumento de la inconsistencia también es un poco discutible. Las etiquetas son campos de texto libre y no se espera ninguna operación como 'cambiar el nombre de todas las etiquetas "foo" a "bar"'.

Entonces tldr: optaría por la solución de dos tablas. (De hecho, lo haré. Encontré este artículo para ver si hay argumentos válidos en contra).

Scheintod avatar Sep 20 '2013 18:09 Scheintod

Si está utilizando una base de datos que admite reducción de mapas, como CouchDB, almacenar etiquetas en un campo de texto sin formato o en un campo de lista es, de hecho, la mejor manera. Ejemplo:

tagcloud: {
  map: function(doc){ 
    for(tag in doc.tags){ 
      emit(doc.tags[tag],1) 
    }
  }
  reduce: function(keys,values){
    return values.length
  }
}

Ejecutar esto con group=true agrupará los resultados por nombre de etiqueta e incluso devolverá un recuento del número de veces que se encontró esa etiqueta. Es muy similar a contar las apariciones de una palabra en un texto .

Nick Retallack avatar Sep 07 '2008 19:09 Nick Retallack

Utilice una única columna de texto formateado[1] para almacenar las etiquetas y utilice un motor de búsqueda de texto completo capaz para indexarlas. De lo contrario, se encontrará con problemas de escala al intentar implementar consultas booleanas.

Si necesita detalles sobre las etiquetas que tiene, puede realizar un seguimiento en una tabla mantenida de forma incremental o ejecutar un trabajo por lotes para extraer la información.

[1] Algunos RDBMS incluso proporcionan un tipo de matriz nativo que podría ser incluso más adecuado para el almacenamiento al no necesitar un paso de análisis, pero podría causar problemas con la búsqueda de texto completo.

David Schmitt avatar Sep 07 '2008 11:09 David Schmitt

Siempre mantuve las etiquetas en una tabla separada y luego tuve una tabla de mapeo. Por supuesto, tampoco he hecho nunca nada a gran escala.

Tener una tabla de "etiquetas" y una tabla de mapas hace que sea bastante trivial generar nubes de etiquetas y demás, ya que puede crear SQL fácilmente para obtener una lista de etiquetas con recuentos de la frecuencia con la que se usa cada etiqueta.

Mark Biek avatar Aug 21 '2008 19:08 Mark Biek