Me preguntaba cuál es la mejor manera de implementar un sistema de etiquetas, como el que se usa en SO. Estaba pensando en esto, pero no puedo encontrar una buena solución escalable.
Estaba pensando en tener una solución básica de 3 mesas: tener una tags
mesa, una articles
mesa y una tag_to_articles
mesa.
¿Es esta la mejor solución a este problema o existen alternativas? Usando este método, la tabla se volvería extremadamente grande con el tiempo, y supongo que para buscar esto no es demasiado eficiente. Por otro lado, no es tan importante que la consulta se ejecute rápidamente.
Respuestas:
Creo que encontrará interesante esta publicación de blog: Etiquetas: esquemas de base de datos
Solución "MySQLicious"
En esta solución, el esquema tiene solo una tabla, está desnormalizado. Este tipo se llama “solución MySQLicious” porque MySQLicious importa datos del.icio.us a una tabla con esta estructura.
Consulta de intersección (Y) para "búsqueda + servicio web + semweb":
Consulta de unión (OR) para "búsqueda | servicio web | semweb":
Menos consulta para "búsqueda + servicio web-semweb"
Solución "Scuttle"
Scuttle organiza sus datos en dos tablas. Esa tabla “scCategories” es la tabla de “etiquetas” y tiene una clave externa para la tabla de “marcadores”.
Consulta de intersección (Y) para "marcador + servicio web + semweb":
Primero, se buscan todas las combinaciones de marcador-etiqueta, donde la etiqueta es "marcador", "servicio web" o "semweb" (c.category IN ('marcador', 'servicio web', 'semweb')), luego solo los marcadores que tienen las tres etiquetas buscadas se tienen en cuenta (HAVING COUNT (b.bId) = 3).
Unión (OR) Consulta de "marcador | servicio web | semweb": simplemente omita la cláusula HAVING y tendrá unión:
Menos (exclusión) Consulta para “marcador + servicio web-semweb”, es decir: marcador Y servicio web Y NO semweb.
Si se omite HAVING COUNT, aparecerá la consulta de "marcador | webservice-semweb".
Solución "Toxi"
Toxi ideó una estructura de tres mesas. A través de la tabla "mapa de etiquetas", los marcadores y las etiquetas están relacionados de n-a-m. Cada etiqueta se puede utilizar junto con diferentes marcadores y viceversa. Wordpress también utiliza este esquema de base de datos. Las consultas son prácticamente las mismas que en la solución “scuttle”.
Consulta de intersección (Y) para "marcador + servicio web + semweb"
Consulta de unión (OR) para "marcador | servicio web | semweb"
Menos (exclusión) Consulta para “marcador + servicio web-semweb”, es decir: marcador Y servicio web Y NO semweb.
Si se omite HAVING COUNT, aparecerá la consulta de "marcador | webservice-semweb".
fuente
No hay nada de malo en su solución de tres mesas.
Otra opción es limitar la cantidad de etiquetas que se pueden aplicar a un artículo (como 5 en SO) y agregarlas directamente a la tabla de artículos.
La normalización de la base de datos tiene sus ventajas e inconvenientes, al igual que el cableado fijo en una tabla tiene sus ventajas y sus inconvenientes.
Nada dice que no puedas hacer ambas cosas. Va en contra de los paradigmas de bases de datos relacionales repetir información, pero si el objetivo es el rendimiento, es posible que deba romper los paradigmas.
fuente
La implementación de tres tablas propuesta funcionará para el etiquetado.
El desbordamiento de pila usa, sin embargo, una implementación diferente. Almacenan etiquetas en la columna varchar en la tabla de publicaciones en texto sin formato y usan la indexación de texto completo para buscar publicaciones que coinciden con las etiquetas. Por ejemplo
posts.tags = "algorithm system tagging best-practices"
. Estoy seguro de que Jeff ha mencionado esto en alguna parte, pero olvido dónde.fuente
La solución propuesta es la mejor, si no la única forma práctica, que se me ocurre para abordar la relación de muchos a muchos entre etiquetas y artículos. Así que mi voto es por 'sí, sigue siendo el mejor'. Aunque estaría interesado en cualquier alternativa.
fuente
Si su base de datos admite matrices indexables (como PostgreSQL, por ejemplo), recomendaría una solución completamente desnormalizada: almacenar etiquetas como una matriz de cadenas en la misma tabla. De lo contrario, la mejor solución es una tabla secundaria que asigne objetos a las etiquetas. Si necesita almacenar información adicional contra las etiquetas, puede usar una tabla de etiquetas separada, pero no tiene sentido introducir una segunda combinación para cada búsqueda de etiquetas.
fuente
Me gustaría sugerir MySQLicious optimizado para un mejor rendimiento. Antes de eso, los inconvenientes de la solución Toxi (3 tablas) son
Si tiene millones de preguntas y tiene 5 etiquetas en cada una, habrá 5 millones de entradas en la tabla de mapa de etiquetas. Entonces, primero tenemos que filtrar 10 mil entradas de mapa de etiquetas basadas en la búsqueda de etiquetas y luego filtrar nuevamente las preguntas coincidentes de esas 10 mil. Entonces, mientras se filtra si la identificación artical es numérica simple, entonces está bien, pero si es una especie de UUID (32 varchar), entonces el filtrado necesita una comparación más grande aunque está indexado.
Mi solución:
Siempre que se cree una nueva etiqueta, tenga un contador ++ (base 10) y convierta ese contador en base64. Ahora cada nombre de etiqueta tendrá una identificación base64. y pasar esta identificación a la interfaz de usuario junto con el nombre. De esta manera, tendrá un máximo de dos ID de caracteres hasta que tengamos 4095 etiquetas creadas en nuestro sistema. Ahora concatene estas múltiples etiquetas en cada columna de etiquetas de la tabla de preguntas. Agregue delimitador también y hágalo ordenado.
Entonces la mesa se ve así
Mientras realiza la consulta, consulte la identificación en lugar del nombre real de la etiqueta. Dado que está CLASIFICADO , la
and
condición en la etiqueta será más eficiente (LIKE '%|a|%|c|%|f|%
).Tenga en cuenta que el delimitador de un solo espacio no es suficiente y necesitamos un delimitador doble para diferenciar las etiquetas como
sql
ymysql
porqueLIKE "%sql%"
también devolveránmysql
resultados. Debiera serLIKE "%|sql|%"
Sé que la búsqueda no está indexada, pero aún así es posible que haya indexado otras columnas relacionadas con el artículo como autor / fecha y hora, de lo contrario, se realizará un escaneo completo de la tabla.
Finalmente, con esta solución, no se requiere unión interna donde se deben comparar millones de registros con 5 millones de registros en condición de unión.
fuente
Notas:
AUTO_INCREMENT
PK sustituta . Por lo tanto, es mejor que Scuttle.LIKE
con el líder de comodín; falsos accesos a subseries)Discusiones relacionadas (para MySQL):
muchas: muchas listas ordenadas de optimización de tabla de mapeo
fuente