Diseño de base de datos SQL recomendado para etiquetas o etiquetado
database-design tags (6)
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, hace que el desarrollo sea mucho más simple: en la solución de tres tablas para insertar y actualizar un item
, debe buscar la tabla de Tag
para ver si ya hay entradas. Entonces tienes que unirte a ellos con otros nuevos. Esta no es una tarea trivial.
Luego hace que las consultas sean más simples (y quizás más rápidas). Hay tres consultas de base de datos principales que hará: generar todas las Tags
para un 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 tomar más espacio en la base de datos (lo que podría llevar a más operaciones de disco, lo que es más lento) y no se normalizará, lo que podría generar inconsistencias.
El argumento de 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 tan grande. Se podría argumentar que la consulta para el título de la etiqueta es mucho más rápida en una pequeña tabla que contiene cada etiqueta solo una vez y esto ciertamente es cierto. Pero teniendo en cuenta los ahorros por no tener que unirte y el hecho de que puedes construir un buen índice en ellos podría compensarlo fácilmente. Por supuesto, esto depende en gran medida del tamaño de la base de datos que está utilizando.
El argumento de inconsistencia es un poco discutible también. Las etiquetas son campos de texto libre y no se espera ninguna operación como ''cambiar el nombre de todas las etiquetas "foo" a "barra"''.
Así que tldr: me gustaría ir a la solución de dos mesas. (De hecho, lo haré. Encontré este artículo para ver si hay argumentos válidos en su contra).
He oído hablar de algunas maneras de implementar el etiquetado; usando una tabla de mapeo entre TagID y ItemID (tiene sentido para mí, pero ¿es escalable?), agregando un número fijo de posibles columnas TagID a ItemID (parece una mala idea), manteniendo las etiquetas en una columna de texto que está separada por comas (suena loco pero podría funcionar). Incluso he escuchado a alguien recomendar una matriz dispersa, pero ¿cómo crecen con gracia los nombres de las etiquetas?
¿Me falta una buena práctica para las etiquetas?
Si está utilizando una base de datos que admite map-reduce, como couchdb, almacenar etiquetas en un campo de texto sin formato o en un campo de lista es 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 conteo de la cantidad de veces que se encontró esa etiqueta. Es muy similar a contar las ocurrencias de una palabra en un texto .
Siempre he mantenido las etiquetas en una tabla separada y luego tuve una tabla de mapeo. Por supuesto, tampoco he hecho nada a gran escala.
Tener una tabla de "etiquetas" y una tabla de mapas hace que sea bastante trivial generar nubes de etiquetas, ya que puede juntar fácilmente SQL para obtener una lista de etiquetas con conteos de la frecuencia con la que se usa cada etiqueta.
Sugeriría el siguiente diseño: Tabla de elementos: Itemid, taglist1, taglist2
esto será rápido y facilitará guardar y recuperar los datos a nivel de artículo.
En paralelo, cree otra tabla: las etiquetas de etiquetas no hacen que la etiqueta sea un identificador único y si se queda sin espacio en la segunda columna que contiene, digamos que 100 elementos crean otra fila.
Ahora, mientras se buscan artículos para una etiqueta, será súper rápido.
Tres tablas (una para almacenar todos los elementos, una para todas las etiquetas y otra para la relación entre las dos), correctamente indexadas, con el conjunto de claves externas que se ejecutan en una base de datos adecuada, deberían funcionar bien y escalarse correctamente.
Table: Item
Columns: ItemID, Title, Content
Table: Tag
Columns: TagID, Title
Table: ItemTag
Columns: ItemID, TagID
Use una columna de texto con formato único [1] para almacenar las etiquetas y use un motor de búsqueda de texto completo capaz de indexar esto. De lo contrario, se encontrará con problemas de escala cuando intente implementar consultas booleanas.
Si necesita detalles sobre las etiquetas que tiene, puede realizar un seguimiento de las mismas en una tabla de mantenimiento incremental o ejecutar un trabajo por lotes para extraer la información.
[1] Algunos RDBMS incluso proporcionan un tipo de matriz nativa que podría ser incluso mejor para el almacenamiento al no necesitar un paso de análisis, pero podría causar problemas con la búsqueda de texto completo.