Etiketleme için Veritabanı Tasarımı


171

Aşağıdaki etiketleme özelliklerini desteklemek için bir veritabanını nasıl tasarlarsınız:

  • öğelerin çok sayıda etiketi olabilir
  • belirli bir etiket kümesiyle etiketlenen tüm öğeleri arar hızlı olmalıdır (öğelerin TÜM etiketleri olması gerekir, bu nedenle bir OR araması değil, bir AND aramasıdır)
  • hızlı arama / okumayı etkinleştirmek için öğe oluşturmak / yazmak daha yavaş olabilir

İdeal olarak, (en azından) bir dizi n etiketle etiketlenmiş tüm öğelerin aranması, tek bir SQL ifadesi kullanılarak yapılmalıdır. Aranacak etiket sayısı ve herhangi bir öğedeki etiket sayısı bilinmediğinden ve yüksek olabileceğinden, JOIN'leri kullanmak pratik değildir.

Herhangi bir fikir?


Şimdiye kadarki tüm cevaplar için teşekkürler.

Ancak yanılmıyorsam verilen cevaplar etiketlerde OR aramasının nasıl yapılacağını gösterir. (Bir veya daha fazla n etiketi olan tüm öğeleri seçin). Verimli bir AND-search arıyorum. (ALL n etiketine ve daha fazlasına sahip tüm öğeleri seçin.)

Yanıtlar:


22

ANDing hakkında: Görünüşe göre "ilişkisel bölünme" işlemini arıyorsunuz. Bu makale ilişkisel bölünmeyi kısa ve anlaşılır bir şekilde ele almaktadır.

Performans hakkında: Bitmap tabanlı bir yaklaşım sezgisel olarak kulağa uygun gibi geliyor. Ancak, digiguru önerdiği gibi, "elle" bitmap indeksleme uygulamak iyi bir fikir olduğuna inanmıyorum: Yeni etiketler eklendiğinde karmaşık bir durum gibi geliyor (?) Ancak bazı DBMS'ler (Oracle dahil) bir şekilde olabilir bitmap dizinleri sunuyor yerleşik bir indeksleme sistemi, indeks bakımının potansiyel karmaşıklığını ortadan kaldırdığından; ayrıca, bitmap dizinleri sunan bir DBMS, sorgu planını gerçekleştirirken bunları uygun bir şekilde değerlendirebilmelidir.


4
Ben veritabanı biraz alan türü kullanarak belirli bir bit sayısı ile sınırlandırır, çünkü cevap biraz kısa görüşlü olduğunu söylemek gerekir. Bu, her öğenin belirli sayıda etiketle sınırlı olduğu anlamına gelmez, ancak tüm sistemde yalnızca belirli sayıda benzersiz etiket olabileceği anlamına gelir (genellikle 32 veya 64'e kadar).
Mark Renouf

1
Question_has_Tag içindeki Tag_id'de 3nf uygulaması (Question, Tag, Question_has_Tag) ve bir bitmap dizini varsayarsak, her soru eklendiğinde veya kaldırıldığında bitmap dizininin yeniden oluşturulması gerekir. Gibi bir sorgu 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)iyi olmalı ve orta masada doğru b-ağacı indeksleri varsayılarak ölçeklendirilmelidir
Adam Musch

"Bu makale" bağlantısı öldü. Bunu okumak isterdim :(
mpen

3
Mark: Bu iyi görünüyor: simple-talk.com/sql/t-sql-programming/… Muhtemelen bahsettiğim kişinin yeniden yayınlanan bir versiyonu.
Troels Arvin

makalenin URL'si artık geçerli değil
Sebastien H.

77

Veritabanı şemalarını etiketlemek için iyi bir makale:

http://howto.philippkeller.com/2005/04/24/Tags-Database-schemas/

performans testleri ile birlikte:

http://howto.philippkeller.com/2005/06/19/Tagsystems-performance-tests/

Buradaki sonuçların (en azından 2005 yılında yazıldığı sırada) çok zayıf tam metin indeksleme özelliklerine sahip olan MySQL'e çok özel olduğunu unutmayın.


1
Ayrıca, SO ile etiketleme sistemini nasıl uyguladığınız hakkında daha ayrıntılı teknik bilgiye sahip olmak isterim? Bence bir podcast her soru ile bir sütunda tüm etiketleri tutmak ve sonra onları seri hale / seri serileştirmek söyledi? Bu konuda daha fazla bilgi edinmek ve belki bazı kod parçacıkları görmek isterim. Etrafa baktım ve herhangi bir ayrıntı buldum, META ile ilgili soruyu sormadan önce bunu yaptığınız bir bağlantı var mı?
Marston A.

5
Meta ile ilgili bu sorunun SO şeması hakkında bazı bilgileri var: meta.stackexchange.com/questions/1863/so-database-schema
Barrett

Orijinal bağlantılar öldü, ama yeni yerlerini bulduğumu düşünüyorum. Bunların bahsettiğiniz makaleler olduğunu doğrulamak isteyebilirsiniz.
Brad Larson

12
@Jeff tarafından yazılmasına rağmen, bu hala bir bağlantı sadece cevabıdır.
curiousdannii

13

Basit bir çözümle ilgili bir sorun görmüyorum: Öğeler için tablo, etiketler için tablo, "etiketleme" için çapraz tablo

Çapraz tablodaki endeksler yeterli optimizasyon olmalıdır. Uygun öğelerin seçilmesi

SELECT * FROM items WHERE id IN  
    (SELECT DISTINCT item_id FROM item_tag WHERE  
    tag_id = tag1 OR tag_id = tag2 OR ...)  

VE etiketleme

SELECT * FROM items WHERE  
    EXISTS (SELECT 1 FROM item_tag WHERE id = item_id AND tag_id = tag1)  
    AND EXISTS (SELECT 1 FROM item_tag WHERE id = item_id AND tag_id = tag2)  
    AND ...

Kuşkusuz, çok sayıda karşılaştırma etiketi için çok verimli değildir. Etiket sayımını bellekte tutacaksanız, sık olmayan etiketlerle başlamak için sorgu yapabilirsiniz, bu nedenle AND sırası daha hızlı değerlendirilir. Eşleştirilecek beklenen etiket sayısına ve bunlardan herhangi birini eşleştirme beklentisine bağlı olarak, 20 etiketle eşleşecek ve bazı rastgele öğelerin 15'le eşleşeceğini umuyorsanız, bu iyi bir çözüm olabilir. bir veritabanında.


13

Sadece @Jeff Atwood'un ( http://howto.philippkeller.com/2005/04/24/Tags-Database-schemas/ ) bağlantısı olan makalenin çok ayrıntılı olduğunu vurgulamak istedim (3 farklı şemanın esasını tartışıyor) yaklaşımları) ve AND sorguları için genellikle şu ana kadar burada belirtilenlerden daha iyi performans gösterecek iyi bir çözüme sahiptir (yani her terim için ilişkili bir alt sorgu kullanmaz). Ayrıca yorumlarda iyi şeyler bir sürü.

ps - Burada herkesin bahsettiği yaklaşım makalede "Toxi" çözümü olarak anılmaktadır.


3
Bu harika makaleyi okuduğumu hatırlıyorum, ancak maalesef bağlantı şimdi öldü. :( Bir ayna biliyor mu?
localhost

5
bağlantı
Aaron

6

Java İçerik Deposu uygulaması (örn. Apache Jackrabbit ) gibi katı veritabanı olmayan bir çözüm denemek ve Apache Lucene gibi bir arama motoru kullanmak isteyebilirsiniz .

Uygun önbellekleme mekanizmalarına sahip bu çözüm, evde yetiştirilen bir çözümden daha iyi performans sağlayacaktır.

Ancak, küçük veya orta ölçekli bir uygulamada, önceki yazılarda belirtilen normalleştirilmiş veritabanından daha karmaşık bir uygulamaya ihtiyacınız olacağını gerçekten düşünmüyorum.

EDIT: açıklama ile bir arama motoru ile JCR benzeri bir çözüm kullanmak daha zor görünüyor. Bu, uzun vadede programlarınızı büyük ölçüde basitleştirecektir.


5

En kolay yöntem bir etiketler tablosu oluşturmaktır .
Target_Type- birden fazla tabloyu
Targetetiketliyorsanız
Tag- Etiketlenen kaydın anahtarı - Bir etiketin metni

Verileri sorgulamak şöyle bir şey olacaktır:

Select distinct target from tags   
where tag in ([your list of tags to search for here])  
and target_type = [the table you're searching]

GÜNCELLEME
VE koşullarına olan gereksiniminize dayanarak, yukarıdaki sorgu böyle bir şeye dönüşecektir

select target
from (
  select target, count(*) cnt 
  from tags   
  where tag in ([your list of tags to search for here])
    and target_type = [the table you're searching]
)
where cnt = [number of tags being searched]

1

Tamamen (R) DB merkezli olmayan bir şey isteyebileceğini @Zizzencs önerisi

Her nasılsa, bu etiketleri bazı uygun önbellekleme / dizinleme ile saklamak için düz nvarchar alanlarını kullanmanın daha hızlı sonuçlar verebileceğine inanıyorum. Ama bu sadece benim.

Daha önce bir çok-çok ilişkiyi temsil etmek için 3 tabloları kullanarak etiketleme sistemleri uyguladım (Öğe Etiketler ItemTags), ancak birçok yerde etiketlerle uğraşacağınızı varsayalım, 3 tablo ile her zaman aynı anda manipüle / sorgulanması kesinlikle kodunuzu daha karmaşık hale getirecektir.

Eklenen karmaşıklığın buna değip değmeyeceğini düşünmek isteyebilirsiniz.


0

Birleştirmeden kaçınamayacaksınız ve yine de normalleştirileceksiniz.

Yaklaşımım bir Tag Tablosuna sahip olmak.

 TagId (PK)| TagName (Indexed)

Ardından, öğeler tablonuzda bir TagXREFID sütunu var.

Bu TagXREFID sütun 3 tablo için bir FK, buna TagXREF diyeceğim:

 TagXrefID | ItemID | TagId

Yani, bir öğe için tüm etiketleri almak şöyle bir şey olurdu:

SELECT Tags.TagId,Tags.TagName 
     FROM Tags,TagXref 
     WHERE TagXref.TagId = Tags.TagId 
         AND TagXref.ItemID = @ItemID

Ve bir etiket için tüm öğeleri almak için böyle bir şey kullanırdım:

SELECT * FROM Items, TagXref
     WHERE TagXref.TagId IN 
          ( SELECT Tags.TagId FROM Tags
                WHERE Tags.TagName = @TagName; )
     AND Items.ItemId = TagXref.ItemId;

VE bir grup etiketi birlikte, AND Tags.TagName = @ TagName1 AND Tags.TagName = @ TagName2 vb ... eklemek için yukarıdaki ifadeyi biraz değiştirin ve sorguyu dinamik olarak oluşturun.


0

Yapmak istediğim şey, ham verileri temsil eden bir dizi tabloya sahip olmaktır, bu nedenle bu durumda

Items (ID pk, Name, <properties>)
Tags (ID pk, Name)
TagItems (TagID fk, ItemID fk)

Bu, yazma süreleri için hızlı çalışır ve her şeyi normalleştirir, ancak her bir etiket için, istediğiniz her etiket için iki kez tablolara katılmanız gerektiğini ve böylece yavaş okunduğunu unutmayın.

Okumayı iyileştirmenin bir çözümü, verileri düzleştirilmiş biçimde temsil eden yeni bir tablo oluşturan saklı bir yordam ayarlayarak komutta bir önbellek tablosu oluşturmaktır ...

CachedTagItems(ID, Name, <properties>, tag1, tag2, ... tagN)

Ardından, Etiketli Öğe tablosunun ne sıklıkta güncel tutulması gerektiğini düşünebilirsiniz, her ekte varsa, bir imleç ekleme etkinliğinde saklı yordamı çağırın. Saatlik bir işse, çalıştırmak için saatlik bir iş ayarlayın.

Şimdi veri alımında gerçekten akıllı olmak için, etiketlerden veri almak üzere saklı bir prosedür oluşturmak isteyeceksiniz. Büyük bir vaka deyiminde iç içe sorgular kullanmak yerine, veritabanından seçmek istediğiniz etiketlerin listesini içeren tek bir parametreyi iletmek ve bir Kayıt Öğeleri kümesi döndürmek istersiniz. Bu en iyi, bitsel işleçler kullanılarak ikili biçimde olur.

İkili biçimde açıklamak kolaydır. Diyelim ki bir öğeye atanacak dört etiket var, ikili dosyada bunu temsil edebiliriz

0000

Dört etiketin tümü bir nesneye atanırsa, nesne şöyle görünür ...

1111

Sadece ilk ikisi ...

1100

O zaman bu sadece istediğiniz sütunda 1'ler ve sıfırlar ile ikili değerleri bulma örneğidir. SQL Server'ın Bitwise operatörlerini kullanarak, çok basit sorgular kullanarak sütunların ilkinde 1 olup olmadığını kontrol edebilirsiniz.

Daha fazla bilgi edinmek için bu bağlantıyı kontrol edin .


0

Başkalarının söylediklerini açıklamak için: hile şemada değil , sorguda .

Varlıkların / Etiketlerin / Etiketlerin naif şeması gitmek için doğru yoldur. Ancak gördüğünüz gibi, çok sayıda etiketle bir VE sorgusunun nasıl gerçekleştirileceği hemen belli değil.

Bu sorguyu optimize etmenin en iyi yolu platforma bağlı olacaktır, bu nedenle sorunuzu RDBS'nizle yeniden etiketlemenizi ve başlığı "etiketleme veritabanında VE sorgusunu gerçekleştirmenin en iyi yolu" gibi bir şeye değiştirmenizi öneririm.

MS SQL için birkaç öneri var, ancak kullandığınız platform değilse kaçınacaktır.


6
Muhtemelen belirli bir teknoloji hakkında bilgi vermekten kaçınmamalısınız, çünkü bu problem alanında çalışmaya çalışan diğer insanlar aslında bu teknolojiyi kullanıyor olabilir ve bundan faydalanacaktır.
Bryan Rehbein

0

Yukarıdaki yanıtın bir varyasyonu, etiket kimliklerini almak, sıralamak, ^ ayrılmış bir dize olarak birleştirmek ve bunları hashlaştırmaktır. Daha sonra hash öğesini öğeyle ilişkilendirin. Her etiket kombinasyonu yeni bir anahtar oluşturur. Bir VE araması yapmak için, verilen etiketi kimlikleri ve aramayı içeren karmayı yeniden oluşturun. Bir öğedeki etiketlerin değiştirilmesi, karmanın yeniden oluşturulmasına neden olur. Aynı etiket grubuna sahip öğeler aynı karma anahtarını paylaşır.


4
Bu yaklaşımla yalnızca aynı etiket kümesine sahip girişleri arayabilirsiniz - bu her zaman önemsizdir. Orijinal sorumda, sorguladığım tüm etiketleri ve muhtemelen daha fazlasını içeren girdileri bulmak istiyorum.
Christian Berg

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.