Design de banco de dados SQL recomendado para tags ou tags [fechado]

288

Eu ouvi algumas maneiras de implementar a marcação; usando uma tabela de mapeamento entre TagID e ItemID (faz sentido para mim, mas será dimensionado?), adicionando um número fixo de possíveis colunas TagID ao ItemID (parece uma má ideia), mantendo as tags em uma coluna de texto separada por vírgulas (sons louco, mas poderia funcionar). Ouvi até alguém recomendar uma matriz esparsa, mas como os nomes das tags crescem normalmente?

Estou perdendo uma prática recomendada para tags?

dlamblin
fonte
9
Ok, esta é a pergunta # 20856, a (quase) mesma pergunta é # 48475 feita pelo menos duas semanas após essa pergunta.
Dlamblin 7/10/08
9
Outra pergunta interessante é "Como o SO implementa tags?"
Mostafa 28/11
1
Outra questão interessante é "Você os internacionalizaria e, se sim, como?"
DanMan
1
Comparação interessante (específica do Postgres): databaseoup.com/2015/01/tag-all-things.html
a_horse_with_no_name

Respostas:

406

Três tabelas (uma para armazenar todos os itens, uma para todas as tags e uma para a relação entre as duas), devidamente indexadas, com chaves estrangeiras definidas em um banco de dados adequado, devem funcionar bem e dimensionar adequadamente.

Table: Item
Columns: ItemID, Title, Content

Table: Tag
Columns: TagID, Title

Table: ItemTag
Columns: ItemID, TagID
Yaakov Ellis
fonte
32
Isso é conhecido como solução “Toxi”, você pode encontrar informações adicionais aqui: howto.philippkeller.com/2005/04/24/Tags-Database-schemas
The Pixel Developer
16
Uma coisa não mostrada aqui são "tags" hierárquicas ou categorias na tabela Tag. Isso geralmente é necessário em sites que possuem categorias e subcategorias, mas precisam da flexibilidade de marcação. Por exemplo, sites de receita, sites de autopeças, diretórios de negócios etc. Esses tipos de dados geralmente não se enquadram em apenas uma única categoria; portanto, a marcação é a resposta, mas você precisa usar algo como o Modelo de Conjunto Aninhado ou o Modelo de Lista de Adjacências na sua tabela de tags.
HK1 21/01
5
Eu concordo com HK1 é possível com a estrutura acima + Tabela: Colunas de TagGroup: TagGropuId, Tabela de título: Colunas de tag: TagID, Title, TagGroupId
Thunder
Quando eu quero adicionar a coluna css à tabela, adicionarei a coluna css na tabela de tags?
Amitābha
10
@ftvs: link novamente quebrado, o novo link é howto.philippkeller.com/2005/04/24/Tags-Database-schemas
Hansaplast
83

Normalmente, eu concordo com Yaakov Ellis, mas neste caso especial, há outra solução viável:

Use duas tabelas:

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

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

Isso tem algumas vantagens principais:

Primeiro, torna o desenvolvimento muito mais simples: na solução de três tabelas para inserção e atualização, itemé necessário pesquisar na Tagtabela para ver se já existem entradas. Então você tem que se juntar a eles com novos. Esta não é uma tarefa trivial.

Em seguida, torna as consultas mais simples (e talvez mais rápidas). Existem três consultas principais ao banco de dados que você fará: Saída Tagsde uma por uma Item, desenhe uma nuvem de tags e selecione todos os itens para um título de tag.

Todas as tags para um item:

3-Tabela:

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

2-Tabela:

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

Nuvem de Tags:

3-Tabela:

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

2-Tabela:

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

Itens para uma etiqueta:

3-Tabela:

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

2-Tabela:

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

Mas também existem algumas desvantagens: poderia levar mais espaço no banco de dados (o que poderia levar a mais operações de disco mais lentas) e não é normalizado, o que poderia levar a inconsistências.

O argumento do tamanho não é tão forte, porque a própria natureza das tags é que elas normalmente são muito pequenas, portanto o aumento do tamanho não é grande. Alguém poderia argumentar que a consulta para o título da tag é muito mais rápida em uma pequena tabela que contém cada tag apenas uma vez e isso certamente é verdade. Mas levar em conta a economia por não ter que ingressar e o fato de que você pode criar um bom índice sobre eles pode compensar isso com facilidade. Obviamente, isso depende muito do tamanho do banco de dados que você está usando.

O argumento da inconsistência também é um pouco discutível. Tags são campos de texto livre e não há operação esperada como 'renomeie todas as tags "foo" para "bar"'.

Então tldr: eu optaria pela solução de duas tabelas. (Na verdade, eu vou. Encontrei este artigo para ver se há argumentos válidos contra ele.)

Scheintod
fonte
"Índice: ItemId, Título" significa um índice para cada um ou um índice contendo ambos?
DanMan
Normalmente dois índices. Porém, pode depender do banco de dados que você está usando.
precisa
1
Na tabela de tags, ItemId e Tag são uma chave composta? ou você também tem um PK?
Rippo 21/03
2
dessa forma, você não pode criar tags "não utilizadas", portanto, um recurso "adicionar tag" deve ser executado em um item. Por outro método, o recurso de "tag add" pode ser realizada de forma independente
Gianluca Ghettini
1
@Quilang. Eu ainda acredito que depende do que você está fazendo :) Eu implementei de duas maneiras em diferentes projetos. No meu último, acabei com uma solução de 3 tabelas porque precisava de um "tipo de tag" (ou alguma outra meta-informação na tag) e poderia reutilizar algum código de um primo próximo de tags: parameters. Mas no mesmo projeto que eu usei exatamente este método para um primo ainda mais perto: bandeiras (por exemplo, 'vendido', 'novo', 'quente'.)
Scheintod
38

Se você estiver usando um banco de dados que suporte a redução de mapa, como couchdb, armazenar tags em um campo de texto sem formatação ou campo de lista é realmente a melhor maneira. Exemplo:

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

Executar isso com group = true agrupará os resultados pelo nome da tag e até retornará uma contagem do número de vezes que a tag foi encontrada. É muito semelhante a contar as ocorrências de uma palavra no texto .

Nick Retallack
fonte
4
+1 É bom ver algumas implementações do NoSQL também.
Xeoncross
@NickRetallack O link não está funcionando. Se você puder, atualize esta resposta.
Xralf
Ok eu ter a ligação com um a archive.org
Nick Retallack
13

Use uma coluna de texto formatado único [1] para armazenar as tags e use um mecanismo de pesquisa de texto completo capaz para indexar isso. Caso contrário, você terá problemas de dimensionamento ao tentar implementar consultas booleanas.

Se você precisar de detalhes sobre as tags que possui, poderá acompanhá-las em uma tabela mantida incrementalmente ou executar um trabalho em lotes para extrair as informações.

[1] Alguns RDBMS fornecem até um tipo de matriz nativa que pode ser ainda mais adequado para armazenamento por não precisar de uma etapa de análise, mas pode causar problemas com a pesquisa de texto completo.

David Schmitt
fonte
Você conhece algum mecanismo de pesquisa de texto completo que não encontra variações em uma palavra? Por exemplo, procurar livro devolve livros? Além disso, o que você faz sobre tags como "c ++"? O SQL Server, por exemplo, removeria os sinais de mais no índice. Obrigado.
Jonathan Wood
Tente Sphinx - sphinxsearch.com
romano
Este tutorial em três partes pode ser útil para quem está seguindo esse caminho (pesquisa de texto completo). Ele está usando as instalações nativas do PostgreSQL: shisaa.jp/postset/postgresql-full-text-search-part-1.html
será
isso é melhor do que a resposta selecionada em termos de desempenho?
Que tal armazenar usando varchar 255, tags separadas por vírgula e adicionar o índice de texto kfull nele?
9

Eu sempre mantive as tags em uma tabela separada e depois tive uma tabela de mapeamento. É claro que também nunca fiz nada em grande escala.

Ter uma tabela "tags" e uma tabela de mapas torna bastante trivial gerar nuvens de tags e outras coisas, pois você pode facilmente montar o SQL para obter uma lista de tags com contagem de quantas vezes cada tag é usada.

Mark Biek
fonte
6
Isto é ainda mais fácil se você não usar uma tabela de mapeamento :)
Scheintod
0

Sugiro o seguinte design: Tabela de itens: Itemid, taglist1, taglist2,
isso será rápido e facilitará o salvamento e a recuperação dos dados no nível do item.

Em paralelo, crie outra tabela: a tag Tags não torna o identificador exclusivo da tag e se você ficar sem espaço na 2ª coluna que contém, digamos 100 itens, crie outra linha.

Agora, ao pesquisar itens para uma tag, será super rápido.

user236575
fonte
en.wikipedia.org/wiki/First_normal_form Embora haja exceções a isso, você pode desnormalizar, mas não aqui #
Dheeraj