Etiketler veya etiketleme için önerilen SQL veritabanı tasarımı [kapalı]


288

Etiketlemeyi uygulamanın birkaç yolunu duydum; TagID ve ItemID arasında bir eşleme tablosu kullanma (bana mantıklı geliyor, ancak ölçekleniyor mu?), ItemID'ye sabit sayıda olası TagID sütunu ekleyerek (kötü bir fikir gibi görünüyor), Etiketleri virgülle ayrılmış bir metin sütununda tutma (sesler) deli ama işe yarayabilir). Birisinin seyrek bir matris önerdiğini bile duydum, ancak etiket adları nasıl zarif bir şekilde büyüyor?

Etiketler için en iyi uygulamayı kaçırıyor muyum?


9
Tamam bu soru # 20856, (neredeyse) aynı soru # 48475 Bu soru sorulduktan en az iki hafta sonra soruldu.
dlamblin

9
Bir başka ilginç soru da "SO etiketleri nasıl uygular?"
Mostafa

1
Bir başka ilginç soru da "Onları uluslararasılaştırır mısınız ve eğer öyleyse, nasıl?"
DanMan

1
İlginç karşılaştırma (Postgres'e özgü): databasesoup.com/2015/01/tag-all-things.html
a_horse_with_no_name

Yanıtlar:


406

Üç tablo (biri tüm öğeleri saklamak için, biri tüm etiketler için, diğeri ikisi arasındaki ilişki için), uygun şekilde dizine alınmış ve uygun bir veritabanında çalışan yabancı anahtarlar ayarlanmış şekilde düzgün çalışmalı ve ölçeklendirilmelidir.

Table: Item
Columns: ItemID, Title, Content

Table: Tag
Columns: TagID, Title

Table: ItemTag
Columns: ItemID, TagID

32
Bu “Toxi” çözümü olarak bilinir, bununla ilgili ek bilgileri burada bulabilirsiniz: howto.philippkeller.com/2005/04/24/Tags-Database-schemas
The Pixel Developer

16
Burada gösterilmeyen bir şey, hiyerarşik "etiketler" veya Etiket tablosundaki kategorilerdir. Bu genellikle kategorileri ve alt kategorileri olan ancak etiketleme esnekliği gerektiren sitelerde gereklidir. Örneğin, reçete siteleri, otomobil parçaları siteleri, işletme dizinleri, vb. Bu tür veriler genellikle yalnızca tek bir kategoriye sığmaz, bu nedenle etiketleme cevaptır, ancak İç İçe Ayarlanmış Model veya Bitişiklik Listesi Modeli gibi bir şey kullanmanız gerekir Etiket tablonuzda.
HK1

5
HK1 ile katılıyorum yukarıdaki yapı ile mümkün + Tablo: TagGroup Sütunlar: TagGropuId, Başlık Tablosu: Etiket Sütunlar: TagID, Başlık, TagGroupId
Thunder

Tabloya css sütun eklemek istediğinizde, etiket tablosuna css sütun ekleyecek?
Amitābha

10
@ftvs: bağlantı tekrar koptu, yeni bağlantı howto.philippkeller.com/2005/04/24/Tags-Database-schemas
hansaplast

83

Normalde Yaakov Ellis ile aynı fikirde olurdum ama bu özel durumda başka bir geçerli çözüm var:

İki tablo kullanın:

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

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

Bunun bazı önemli avantajları vardır:

İlk olarak geliştirme çok daha basit hale gelir: ekleme ve güncelleme için üç tablolu çözümde , zaten giriş olup olmadığını görmek itemiçin Tagtabloya bakmanız gerekir . O zaman onlara yenileriyle katılmak zorundasınız. Bu önemsiz bir görev değil.

Sonra sorguları basitleştirir (ve belki de daha hızlıdır). Yapacağınız üç büyük veritabanı sorgusu vardır: TagsBiri için çıktı alın Item, bir Tag-Cloud çizin ve bir Etiket Başlığı için tüm öğeleri seçin.

Bir Öğe için tüm Etiketler:

3-Tablosu:

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

2-Tablosu:

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

Etiket Bulutu:

3-Tablosu:

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

2-Tablosu:

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

Bir Etiket için öğeleri:

3-Tablosu:

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

2-Tablosu:

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

Ancak bazı dezavantajlar da var: Veritabanında daha fazla yer kaplayabilir (bu da daha yavaş disk işlemlerine yol açabilir) ve normalleşmez ve bu da tutarsızlıklara yol açabilir.

Boyut argümanı o kadar güçlü değildir, çünkü etiketlerin doğası normalde oldukça küçük olmalarıdır, böylece boyut artışı büyük değildir. Her bir etiketi sadece bir kez içeren küçük bir tabloda etiket başlığı sorgusunun çok daha hızlı olduğu iddia edilebilir ve bu kesinlikle doğrudur. Ancak katılmak zorunda kalmamanız için yapılan tasarruflar ve iyi bir endeks oluşturabileceğiniz gerçeği göz önüne alındığında bunu kolayca telafi edebilirsiniz. Bu elbette büyük ölçüde kullandığınız veritabanının boyutuna bağlıdır.

Tutarsızlık argümanı da biraz tartışmalı. Etiketler serbest metin alanlarıdır ve 'tüm etiketleri "foo" olarak "bar" olarak yeniden adlandırmak gibi beklenen bir işlem yoktur.

Yani tldr: İki tablolu çözümü tercih ederim. (Aslında yapacağım. Bu makaleye karşı geçerli argümanlar olup olmadığını görmek için buldum.)


"Dizin: ItemId, Başlık" her biri veya her ikisini içeren bir dizin için bir dizin anlamına mı geliyor?
DanMan

Normalde iki dizin. Yine de kullandığınız veritabanına bağlı olabilir.
Scheintod

1
Tag tablosunda ItemId ve Tag bir bileşik anahtar? ya da bir PK'nız var mı?
Rippo

2
bu şekilde "kullanılmayan" etiketler oluşturamazsınız, böylece bir Öğe üzerinde "etiket ekle" özelliğinin gerçekleştirilmesi gerekir. Diğer yöntemde, "etiket ekle" özelliği bağımsız olarak gerçekleştirilebilir
Gianluca Ghettini

1
@Quilang. Hala ne yaptığınıza bağlı olduğuna inanıyorum :) Farklı projelerde her iki şekilde de uyguladım. Benim son bir 3 tablo çözüm ile sona erdi çünkü ben bir "tag-type" (ya da etiket üzerinde başka bir meta bilgi) ve etiketleri: parametreler yakın bir kuzen bazı kod yeniden kullanabilirsiniz. Ama aynı projede daha da yakın bir kuzen için tam olarak bu yöntemi kullandım: bayraklar (örneğin, 'satıldı', 'yeni', 'sıcak')
Scheintod

38

Couchdb gibi harita azaltmayı destekleyen bir veritabanı kullanıyorsanız, etiketleri düz metin alanında veya liste alanında depolamak gerçekten en iyi yoldur. Misal:

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

Bunu group = true ile çalıştırmak sonuçları etiket adına göre gruplandırır ve hatta bu etiketle kaç kez karşılaşıldığını döndürür. Metindeki bir kelimenin oluşumlarını saymaya çok benzer .


4
+1 Ayrıca bazı NoSQL uygulamalarını görmek güzel.
Xeoncross

@NickRetallack Bağlantı çalışmıyor. Yapabiliyorsanız, lütfen bu yanıtı güncelleyin.
xralf

Tamam linkini archive.org yerine biriyle değiştirdim
Nick Retallack

13

Etiketleri saklamak için tek bir biçimlendirilmiş metin sütunu [1] kullanın ve bunu dizine eklemek için yetenekli bir tam metin arama motoru kullanın. Yoksa, boole sorguları uygulamaya çalışırken ölçeklendirme sorunlarıyla karşılaşırsınız.

Sahip olduğunuz etiketlerle ilgili ayrıntılara ihtiyacınız varsa, etiketi artımlı olarak tutulan bir tabloda izleyebilir veya bilgileri ayıklamak için bir toplu iş çalıştırabilirsiniz.

[1] Bazı RDBMS, ayrıştırma adımına gerek kalmadan depolama için daha uygun olabilecek, ancak tam metin aramasında sorunlara neden olabilecek yerel bir dizi türü bile sağlar.


Bir kelimede varyasyon bulamayan herhangi bir tam metin arama motorunun farkında mısınız? Örneğin, kitap mı arıyorsunuz? Ayrıca, "c ++" gibi etiketler hakkında ne yaparsınız? Örneğin SQL Server, dizindeki artı işaretlerini kaldırır. Teşekkürler.
Jonathan Wood

Sfenks'i deneyin - sphinxsearch.com
Roman

Bu 3 parçalı eğitim, bu rotayı kullananlar için yararlı olabilir (tam metin araması). Bu PostgreSQL yerli imkanlar kullanılarak açıklanmıştır: shisaa.jp/postset/postgresql-full-text-search-part-1.html
Will

performans açısından seçilen cevaptan daha mı iyi?

varchar 255, virgülle ayrılmış etiketler kullanarak depolamaya ve üzerine kfull metin dizini eklemeye ne dersiniz?

9

Etiketleri her zaman ayrı bir tabloda tuttum ve daha sonra bir eşleme tablosum vardı. Tabii ki ben de gerçekten büyük ölçekte hiçbir şey yapmadım.

Bir "etiketleri" tablosu ve bir harita tablosu olması, etiket bulutları oluşturmayı oldukça önemsiz hale getirir, çünkü her bir etiketin ne sıklıkta kullanıldığına dair bir etiket listesi almak için SQL'i kolayca bir araya getirebilirsiniz.


6
Bir eşleme tablosu kullanmıyorsanız bu daha da kolaydır :)
Scheintod

0

Aşağıdaki tasarımı öneririm: Öğe Tablosu: Itemid, taglist1, taglist2
bu hızlı olacak ve öğe düzeyinde verileri kaydetmeyi ve almayı kolaylaştıracaktır.

Paralel olarak başka bir tablo oluşturun: Tags etiketi, etiketi benzersiz tanımlayıcı yapmaz ve 2. sütunda yer kalmazsa, 100 öğenin başka bir satır oluşturmasına izin verir.

Şimdi bir etiket için öğeleri ararken süper hızlı olacak.


en.wikipedia.org/wiki/First_normal_form Bunun istisnaları olmasına rağmen, normalleştirebilirsiniz, ancak burada değil
Dheeraj
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.