¿Cómo diseñaría una base de datos para admitir las siguientes características de etiquetado:
- los elementos pueden tener una gran cantidad de etiquetas
- Las búsquedas de todos los elementos que están etiquetados con un conjunto determinado de etiquetas deben ser rápidas (los elementos deben tener TODAS las etiquetas, por lo que es una búsqueda AND, no una búsqueda OR)
- crear / escribir elementos puede ser más lento para permitir una búsqueda / lectura rápida
Idealmente, la búsqueda de todos los elementos que están etiquetados con (al menos) un conjunto de n etiquetas dadas debe hacerse usando una sola instrucción SQL. Dado que se desconoce el número de etiquetas para buscar, así como el número de etiquetas en cualquier elemento y puede ser alto, no es práctico usar JOIN.
¿Algunas ideas?
Gracias por todas las respuestas hasta el momento.
Sin embargo, si no me equivoco, las respuestas dadas muestran cómo hacer una búsqueda OR en las etiquetas. (Seleccione todos los elementos que tengan una o más de n etiquetas). Estoy buscando una eficiente búsqueda AND. (Seleccione todos los elementos que tengan TODAS las etiquetas n, y posiblemente más).
fuente
select * from question q inner join question_has_tag qt where tag_id in (select tag_id from tags where (what we want) minus select tag_id from tags where (what we don't)
debería estar bien y escalar asumiendo que existen los índices b-tree correctos en la tabla centralAquí hay un buen artículo sobre etiquetado de esquemas de bases de datos:
http://howto.philippkeller.com/2005/04/24/Tags-Database-schemas/
junto con pruebas de rendimiento:
http://howto.philippkeller.com/2005/06/19/Tagsystems-performance-tests/
Tenga en cuenta que las conclusiones allí son muy específicas para MySQL, que (al menos en 2005 en el momento en que se escribió) tenía características de indexación de texto completo muy pobres.
fuente
No veo un problema con una solución sencilla: tabla para elementos, tabla para etiquetas, tabla cruzada para "etiquetado"
Los índices en la tabla cruzada deberían ser suficiente optimización. Seleccionar elementos apropiados sería
Y el etiquetado sería
lo cual es cierto, no es tan eficiente para un gran número de etiquetas de comparación. Si desea mantener el recuento de etiquetas en la memoria, puede hacer que la consulta comience con etiquetas que no son frecuentes, por lo que la secuencia AND se evaluaría más rápidamente. Dependiendo del número esperado de etiquetas con las que se comparará y la expectativa de que coincida con cualquiera de ellas, esta podría ser una buena solución, si va a hacer coincidir 20 etiquetas y espera que algún elemento aleatorio coincida con 15 de ellas, entonces esto aún sería pesado en una base de datos
fuente
Solo quería resaltar que el artículo al que se vincula @Jeff Atwood ( http://howto.philippkeller.com/2005/04/24/Tags-Database-schemas/ ) es muy exhaustivo (analiza los méritos de 3 esquemas diferentes enfoques) y tiene una buena solución para las consultas AND que generalmente funcionarán mejor de lo que se ha mencionado aquí hasta ahora (es decir, no utiliza una subconsulta correlacionada para cada término). También muchas cosas buenas en los comentarios.
ps: el enfoque del que todos hablan aquí se conoce como la solución "Toxi" en el artículo.
fuente
Es posible que desee experimentar con una solución no estrictamente de base de datos, como una implementación de Java Content Repository (por ejemplo, Apache Jackrabbit ) y utilizar un motor de búsqueda creado además de eso como Apache Lucene .
Esta solución con los mecanismos de almacenamiento en caché apropiados posiblemente produciría un mejor rendimiento que una solución local.
Sin embargo, realmente no creo que en una aplicación pequeña o mediana requiera una implementación más sofisticada que la base de datos normalizada mencionada en publicaciones anteriores.
EDITAR: con su aclaración, parece más convincente utilizar una solución similar a JCR con un motor de búsqueda. Eso simplificaría enormemente sus programas a largo plazo.
fuente
El método más fácil es crear una tabla de etiquetas .
Target_Type
- en caso de que esté etiquetando varias tablasTarget
- La clave del registro que se está etiquetandoTag
- El texto de una etiquetaConsultar los datos sería algo como:
ACTUALIZACIÓN
Según su requisito de Y las condiciones, la consulta anterior se convertiría en algo como esto
fuente
En segundo lugar, sugiero a @Zizzencs que tal vez quieras algo que no esté totalmente centrado en (R) DB
De alguna manera, creo que el uso de campos simples de nvarchar para almacenar esas etiquetas con un almacenamiento en caché / indexación adecuado podría producir resultados más rápidos. Pero solo soy yo.
He implementado sistemas de etiquetado usando 3 tablas para representar una relación de muchos a muchos antes (Item Tags ItemTags), pero supongo que tratarás con etiquetas en muchos lugares, puedo decirte que con 3 tablas tienes que ser manipulado / consultado simultáneamente todo el tiempo definitivamente hará que su código sea más complejo.
Es posible que desee considerar si la complejidad adicional lo vale.
fuente
No podrá evitar las uniones y aún así estar algo normalizado.
Mi enfoque es tener una tabla de etiquetas.
Luego, tiene una columna TagXREFID en su tabla de artículos.
Esta columna TagXREFID es un FK a una tercera tabla, la llamaré TagXREF:
Entonces, obtener todas las etiquetas para un artículo sería algo como:
Y para obtener todos los elementos para una etiqueta, usaría algo como esto:
Para AND un montón de etiquetas juntas, debe modificar ligeramente la declaración anterior para agregar AND Tags.TagName = @ TagName1 AND Tags.TagName = @ TagName2, etc. ... y generar dinámicamente la consulta.
fuente
Lo que me gusta hacer es tener una serie de tablas que representen los datos sin procesar, por lo que en este caso tendrías
Esto funciona rápido para los tiempos de escritura y mantiene todo normalizado, pero también puede tener en cuenta que para cada etiqueta, deberá unir las tablas dos veces por cada etiqueta adicional que desee Y, por lo que tiene una lectura lenta.
Una solución para mejorar la lectura es crear una tabla de almacenamiento en caché por comando configurando un procedimiento almacenado que esencialmente crea una nueva tabla que representa los datos en un formato plano ...
Luego, puede considerar con qué frecuencia la tabla de Elementos etiquetados debe mantenerse actualizada, si está en cada inserción, luego llame al procedimiento almacenado en un evento de inserción de cursor. Si es una tarea por hora, configure un trabajo por hora para ejecutarla.
Ahora, para ser realmente inteligente en la recuperación de datos, querrá crear un procedimiento almacenado para obtener datos de las etiquetas. En lugar de utilizar consultas anidadas en una declaración de caso masiva, desea pasar un único parámetro que contenga una lista de etiquetas que desea seleccionar de la base de datos y devolver un conjunto de elementos de registro. Esto sería mejor en formato binario, utilizando operadores bit a bit.
En formato binario, es fácil de explicar. Digamos que hay cuatro etiquetas para asignar a un elemento, en binario podríamos representar que
Si las cuatro etiquetas se asignan a un objeto, el objeto se vería así ...
Si solo los dos primeros ...
Entonces es solo un caso de encontrar los valores binarios con los 1s y ceros en la columna que desee. Usando los operadores Bitwise de SQL Server, puede verificar que haya un 1 en la primera de las columnas usando consultas muy simples.
Consulte este enlace para obtener más información .
fuente
Parafraseando lo que otros han dicho: el truco no está en el esquema , está en la consulta .
El ingenuo esquema de Entidades / Etiquetas / Etiquetas es el camino correcto. Pero como ha visto, no está claro de inmediato cómo realizar una consulta AND con muchas etiquetas.
La mejor manera de optimizar esa consulta dependerá de la plataforma, por lo que recomendaría volver a etiquetar su pregunta con su RDBS y cambiar el título a algo así como "Forma óptima de realizar Y consultar en una base de datos de etiquetado".
Tengo algunas sugerencias para MS SQL, pero me abstendré en caso de que no sea la plataforma que está utilizando.
fuente
Una variación de la respuesta anterior es tomar los identificadores de etiqueta, ordenarlos, combinarlos como una cadena ^ separada y hacerlos hash. Luego, simplemente asocia el hash al elemento. Cada combinación de etiquetas produce una nueva clave. Para hacer una búsqueda AND, simplemente vuelva a crear el hash con los identificadores de etiqueta dados y busque. Cambiar las etiquetas de un elemento hará que se vuelva a crear el hash. Los elementos con el mismo conjunto de etiquetas comparten la misma clave hash.
fuente
Si tiene un tipo de matriz, puede agregar previamente los datos necesarios. Vea esta respuesta en un hilo separado:
¿Cuál es la utilidad del tipo de matriz?
fuente