Como você projetaria um banco de dados para suportar os seguintes recursos de marcação:
- itens podem ter um grande número de tags
- as pesquisas de todos os itens marcados com um determinado conjunto de tags devem ser rápidas (os itens devem ter TODAS as tags, portanto, é uma pesquisa AND, não uma pesquisa OR)
- a criação / gravação de itens pode ser mais lenta para permitir pesquisa / leitura rápida
Idealmente, a pesquisa de todos os itens marcados com (pelo menos) um conjunto de n tags deve ser feita usando uma única instrução SQL. Como o número de tags a serem pesquisadas e o número de tags em qualquer item são desconhecidos e podem ser altos, o uso de JOINs é impraticável.
Alguma ideia?
Obrigado por todas as respostas até agora.
Se não me engano, no entanto, as respostas fornecidas mostram como fazer uma pesquisa em OR nas tags. (Selecione todos os itens que possuem uma ou mais de n tags). Estou à procura de uma eficiente E-pesquisa. (Selecione todos os itens que possuem TODAS tags n - e possivelmente mais.)
fonte
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)
deve ser fina e escalar assumindo existem os índices b-árvore certa no meio mesaAqui está um bom artigo sobre como marcar esquemas de banco de dados:
http://howto.philippkeller.com/2005/04/24/Tags-Database-schemas/
junto com os testes de desempenho:
http://howto.philippkeller.com/2005/06/19/Tagsystems-performance-tests/
Observe que as conclusões são muito específicas para o MySQL, que (pelo menos em 2005 na época em que foi escrito) tinha características de indexação de texto completo muito ruins.
fonte
Não vejo problema com uma solução simples: tabela para itens, tabela para tags, cruzável para "marcação"
Os índices na tabela cruzada devem ter otimização suficiente. A seleção de itens apropriados seria
E a marcação seria
que é reconhecidamente não tão eficiente para um grande número de tags de comparação. Se você deseja manter a contagem de tags na memória, é possível fazer uma consulta para começar com tags que não são frequentes, para que a sequência AND seja avaliada mais rapidamente. Dependendo do número esperado de tags a serem comparadas e da expectativa de corresponder a qualquer uma delas, isso pode ser uma solução OK. Se você quiser combinar 20 tags e esperar que algum item aleatório corresponda a 15 delas, isso ainda será pesado em um banco de dados.
fonte
Eu só queria destacar que o artigo ao qual @Jeff Atwood se vincula ( http://howto.philippkeller.com/2005/04/24/Tags-Database-schemas/ ) é muito completo (discute os méritos de três esquemas diferentes abordagens) e possui uma boa solução para as consultas AND, que normalmente têm um desempenho melhor do que o mencionado aqui até agora (ou seja, não usa uma subconsulta correlacionada para cada termo). Também muita coisa boa nos comentários.
ps - A abordagem que todo mundo está falando aqui é referida como a solução "Toxi" no artigo.
fonte
Você pode experimentar uma solução não estritamente de banco de dados como uma implementação do Java Content Repository (por exemplo, Apache Jackrabbit ) e usar um mecanismo de pesquisa construído sobre ele como o Apache Lucene .
Essa solução com os mecanismos de armazenamento em cache apropriados possivelmente produziria melhor desempenho do que uma solução doméstica.
No entanto, eu realmente não acho que em um aplicativo pequeno ou médio você exija uma implementação mais sofisticada do que o banco de dados normalizado mencionado nas postagens anteriores.
EDIT: com seu esclarecimento, parece mais atraente usar uma solução semelhante ao JCR com um mecanismo de pesquisa. Isso simplificaria bastante seus programas a longo prazo.
fonte
O método mais fácil é criar uma tabela de tags .
Target_Type
- no caso de você estar marcando várias tabelasTarget
- A chave do registro que está sendo marcadoTag
- O texto de uma marcaConsultar os dados seria algo como:
ATUALIZAÇÃO
Com base em sua exigência de AND nas condições, a consulta acima se tornaria algo como isto
fonte
Gostaria da segunda sugestão do @Zizzencs de que você pode querer algo que não seja totalmente centralizado no DB
De alguma forma, acredito que o uso de campos nvarchar simples para armazenar essas tags com algum cache / indexação adequado pode gerar resultados mais rápidos. Mas sou só eu.
Eu implementei sistemas de marcação usando 3 tabelas para representar um relacionamento Muitos-para-Muitos antes (Item Tags ItemTags), mas suponho que você esteja lidando com tags em muitos lugares, posso dizer que com 3 tabelas ser manipulado / consultado simultaneamente o tempo todo definitivamente tornará seu código mais complexo.
Você pode considerar se a complexidade adicionada vale a pena.
fonte
Você não poderá evitar junções e ainda será um pouco normalizado.
Minha abordagem é ter uma tabela de tags.
Então, você tem uma coluna TagXREFID na sua tabela de itens.
Esta coluna TagXREFID é um FK para uma 3ª tabela, chamarei de TagXREF:
Portanto, obter todas as tags de um item seria algo como:
E para obter todos os itens para uma tag, eu usaria algo como isto:
Para AND um monte de tags juntos, você deve modificar ligeiramente a instrução acima para adicionar AND Tags.TagName = @ TagName1 AND Tags.TagName = @ TagName2 etc ... e criar dinamicamente a consulta.
fonte
O que eu gosto de fazer é ter um número de tabelas que representam os dados brutos, portanto, nesse caso, você teria
Isso funciona rápido para os tempos de gravação e mantém tudo normalizado, mas você também pode observar que, para cada tag, você precisará ingressar nas tabelas duas vezes para cada tag adicional que desejar AND, para uma leitura lenta.
Uma solução para melhorar a leitura é criar uma tabela de cache sob comando, configurando um procedimento armazenado que essencialmente cria uma nova tabela que representa os dados em um formato nivelado ...
Em seguida, considere com que frequência a tabela Item marcado precisa ser atualizada, se estiver em todas as inserções, e chame o procedimento armazenado em um evento de inserção do cursor. Se for uma tarefa horária, configure um trabalho por hora para executá-lo.
Agora, para ser realmente inteligente na recuperação de dados, você desejará criar um procedimento armazenado para obter dados das tags. Em vez de usar consultas aninhadas em uma declaração de caso maciça, você deseja passar um único parâmetro que contém uma lista de tags que deseja selecionar no banco de dados e retornar um conjunto de itens de registro. Isso seria melhor em formato binário, usando operadores bit a bit.
Em formato binário, é fácil de explicar. Digamos que há quatro tags a serem atribuídas a um item, em binário poderíamos representar isso
Se todas as quatro tags forem atribuídas a um objeto, o objeto ficaria assim ...
Se apenas os dois primeiros ...
Então é apenas um caso de encontrar os valores binários com os 1s e zeros na coluna desejada. Usando os operadores Bitwise do SQL Server, você pode verificar se existe um 1 na primeira das colunas usando consultas muito simples.
Verifique este link para saber mais .
fonte
Parafraseando o que os outros disseram: o truque não está no esquema , está na consulta .
O esquema ingênuo de Entidades / Etiquetas / Tags é o caminho certo a seguir. Mas, como você viu, não está claro imediatamente como executar uma consulta AND com muitas tags.
A melhor maneira de otimizar essa consulta dependerá da plataforma, portanto, recomendo que você remarque sua pergunta com seu RDBS e altere o título para algo como "Maneira ideal de executar E consultar em um banco de dados de marcação".
Tenho algumas sugestões para o MS SQL, mas evitarei se essa não for a plataforma que você está usando.
fonte
Uma variação da resposta acima é pegar os IDs das tags, classificá-los, combinar como uma sequência ^ separada e hash-los. Em seguida, basta associar o hash ao item. Cada combinação de tags produz uma nova chave. Para fazer uma pesquisa AND, simplesmente recrie o hash com os IDs de tags e a pesquisa fornecidos. A alteração de tags em um item fará com que o hash seja recriado. Itens com o mesmo conjunto de tags compartilham a mesma chave de hash.
fonte
Se você possui um tipo de matriz, pode agregar previamente os dados necessários. Veja esta resposta em um tópico separado:
qual é a utilidade do tipo de matriz?
fonte