การออกแบบฐานข้อมูลสำหรับการติดแท็ก


171

คุณจะออกแบบฐานข้อมูลเพื่อสนับสนุนคุณสมบัติการแท็กต่อไปนี้ได้อย่างไร:

  • รายการอาจมีแท็กจำนวนมาก
  • ค้นหารายการทั้งหมดที่ติดแท็กด้วยชุดแท็กที่กำหนดจะต้องเป็นแบบด่วน (รายการนั้นต้องมีแท็กทั้งหมดดังนั้นจึงเป็นการค้นหาแบบ AND ไม่ใช่การค้นหาแบบ OR)
  • การสร้าง / การเขียนรายการอาจช้าลงเพื่อเปิดใช้การค้นหา / อ่านอย่างรวดเร็ว

ตามหลักการแล้วการค้นหารายการทั้งหมดที่ติดแท็กด้วย (อย่างน้อย) ชุดของแท็กที่กำหนดควรทำโดยใช้คำสั่ง SQL เดี่ยว เนื่องจากไม่ทราบจำนวนแท็กรวมถึงจำนวนแท็กในรายการใด ๆ และอาจสูงดังนั้นการใช้ JOIN จึงเป็นไปไม่ได้

ความคิดใด ๆ


ขอบคุณสำหรับคำตอบทั้งหมด

อย่างไรก็ตามหากฉันไม่เข้าใจผิดคำตอบที่ระบุจะแสดงวิธีการค้นหาในแท็ก (เลือกรายการทั้งหมดที่มีแท็กหนึ่งรายการขึ้นไป) ฉันกำลังมองหาที่มีประสิทธิภาพและค้นหา (เลือกรายการทั้งหมดที่มีแท็ก n ทั้งหมด - และอาจมากกว่า)

คำตอบ:


22

เกี่ยวกับ ANDing: ดูเหมือนว่าคุณกำลังมองหาการดำเนินการ "แผนกสัมพันธ์" บทความนี้ครอบคลุมถึงการแบ่งเชิงสัมพันธ์อย่างกระชับและเข้าใจได้ง่าย

เกี่ยวกับประสิทธิภาพ: วิธีการที่ใช้บิตแมปตามสัญชาตญาณดูเหมือนจะเหมาะกับสถานการณ์ได้ดี อย่างไรก็ตามฉันไม่มั่นใจว่าเป็นความคิดที่ดีที่จะใช้การจัดทำดัชนีบิตแมป "ด้วยตนเอง" เช่น digiguru แนะนำ: ดูเหมือนว่าสถานการณ์ที่ซับซ้อนเมื่อมีการเพิ่มแท็กใหม่ (?) แต่ DBMS บางตัว (รวมถึง Oracle) เสนอดัชนีบิตแมป ใช้งานได้เนื่องจากระบบการจัดทำดัชนีในตัวนั้นมีความซับซ้อนที่อาจเกิดขึ้นจากการบำรุงรักษาดัชนี นอกจากนี้ DBMS ที่เสนอดัชนีบิตแมปควรจะสามารถพิจารณาได้อย่างเหมาะสมเมื่อดำเนินการตามแผนแบบสอบถาม


4
ฉันต้องบอกว่าคำตอบนั้นค่อนข้างสั้นเพราะใช้ bit field type ของฐานข้อมูลเพื่อ จำกัด จำนวนบิตของคุณ นี่ไม่ได้หมายความว่าแต่ละไอเท็มจะถูก จำกัด ด้วยจำนวนแท็กที่แน่นอน แต่สามารถมีแท็กที่ไม่ซ้ำกันได้จำนวนหนึ่งเท่านั้นในระบบทั้งหมด (โดยปกติจะมีมากถึง 32 หรือ 64)
Mark Renouf

1
สมมติว่ามีการใช้งาน 3nf (คำถาม, แท็ก, คำถาม _has_Tag) และดัชนีบิตแมปใน Tag_id ใน Question_has_Tag ดัชนีบิตแมปจะต้องสร้างใหม่ทุกครั้งที่มีแท็กเพิ่มหรือลบ ข้อความค้นหา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)ควรมีขนาดเล็กและขยายออกโดยสมมติว่าดัชนี b-tree ถูกต้องอยู่บนโต๊ะกลาง
Adam Musch

ลิงก์ "บทความนี้" ไม่ทำงาน ฉันชอบที่จะอ่านว่า :(
mpen

3
ทำเครื่องหมาย: อันนี้ดูดี: simple-talk.com/sql/t-sql-programming/ ......มันอาจจะเป็นเวอร์ชั่นที่ตีพิมพ์ใหม่ของรุ่นที่ผมอ้างถึง
Troels Arvin

URL ของบทความไม่ถูกต้องอีกต่อไป
Sebastien H.

77

นี่เป็นบทความที่ดีเกี่ยวกับการติดแท็กสกีมาฐานข้อมูล:

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

พร้อมกับการทดสอบประสิทธิภาพ:

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

โปรดทราบว่าข้อสรุปมีเฉพาะเจาะจงมากกับ MySQL ซึ่ง (อย่างน้อยในปี 2005 ในขณะที่เขียน) มีลักษณะการจัดทำดัชนีข้อความที่ไม่ดีมาก


1
ฉันยินดีที่จะให้ข้อมูลเชิงลึกทางเทคนิคที่ละเอียดยิ่งขึ้นเกี่ยวกับวิธีที่คุณใช้ระบบการแท็กด้วย SO ฉันคิดว่าพอดแคสต์คุณบอกว่าคุณเก็บแท็กทั้งหมดไว้ในคอลัมน์ด้วยคำถามทุกข้อแล้วจึงทำให้เป็นอันดับ / ยกเลิกการทำให้เป็นอันดับในทันที ฉันชอบที่จะรู้เพิ่มเติมเกี่ยวกับมันและอาจจะเห็นบางส่วนของรหัส ฉันได้ดูไปรอบ ๆ และพบรายละเอียดใด ๆ มีลิงก์ที่คุณได้ทำไปแล้วก่อนที่ฉันจะถามคำถามเกี่ยวกับ META หรือไม่?
Marston A.

5
คำถามนี้เกี่ยวกับ Meta มีข้อมูลบางอย่างเกี่ยวกับ schema SO: meta.stackexchange.com/questions/1863/so-database-schema
Barrett

ลิงก์เดิมตายไปแล้ว แต่ฉันคิดว่าฉันพบตำแหน่งใหม่ของพวกเขาแล้ว คุณอาจต้องการตรวจสอบว่าสิ่งเหล่านี้เป็นบทความที่คุณอ้างถึง
แบรด Larson

12
แม้จะถูกเขียนโดย @Jeff แต่ก็ยังคงเป็นลิงก์เพียงตอบเท่านั้น
อยากรู้อยากเห็น dannii

13

ฉันไม่เห็นปัญหาเกี่ยวกับวิธีแก้ปัญหาที่ตรงไปตรงมา: ตารางสำหรับรายการ, ตารางสำหรับแท็ก, crosstable สำหรับ "แท็ก"

ดัชนีบนตารางไขว้ควรมีการปรับให้เหมาะสมเพียงพอ การเลือกรายการที่เหมาะสมจะเป็น

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

และการติดแท็กจะเป็น

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 ...

ซึ่งเป็นที่ยอมรับไม่มีประสิทธิภาพสำหรับแท็กเปรียบเทียบจำนวนมาก หากคุณต้องรักษาจำนวนแท็กในหน่วยความจำคุณสามารถสร้างเคียวรีเพื่อเริ่มต้นด้วยแท็กที่ไม่บ่อยดังนั้นลำดับ AND จะถูกประเมินเร็วขึ้น ขึ้นอยู่กับจำนวนแท็กที่คาดหวังที่จะจับคู่กับและความคาดหวังของการจับคู่ใด ๆ ของแท็กนี้อาจเป็นคำตอบที่ดีถ้าคุณจะจับคู่ 20 แท็กและคาดว่ารายการสุ่มบางรายการจะจับคู่ 15 แท็ก บนฐานข้อมูล


13

ฉันต้องการเน้นว่าบทความที่ @Jeff Atwood เชื่อมโยงกับ ( http://howto.philippkeller.com/2005/04/24/Tags-Database-schemas/) ) นั้นละเอียดมาก (อธิบายถึงข้อดีของ 3 schema ที่แตกต่างกัน วิธีการ) และมีวิธีแก้ปัญหาที่ดีสำหรับคำสั่ง AND ซึ่งโดยปกติแล้วจะทำงานได้ดีกว่าที่กล่าวมาแล้ว (เช่นไม่ได้ใช้แบบสอบถามย่อยที่สัมพันธ์กันสำหรับแต่ละคำ) นอกจากนี้ยังมีสิ่งที่ดีมากมายในการแสดงความคิดเห็น

ป.ล. - วิธีการที่ทุกคนกำลังพูดถึงที่นี่เรียกว่าวิธีการแก้ปัญหา "Toxi" ในบทความ


3
ฉันจำได้ว่าอ่านบทความที่ดี แต่น่าเสียดายที่ลิงค์นั้นตายไปแล้ว :( ทราบว่าทุกคนของกระจกของมันได้หรือไม่
localhost

5
ลิงก์นั้นตายแล้ว: <
แอรอน

6

คุณอาจต้องการที่จะทดสอบกับวิธีการแก้ปัญหาที่ไม่เคร่งครัดฐานข้อมูลเช่นJava Content Repositoryการดำเนินงาน (เช่นApache Jackrabbit ) และใช้เครื่องมือค้นหาที่สร้างขึ้นบนด้านบนของที่เช่นApache Lucene

การแก้ปัญหานี้ด้วยกลไกการแคชที่เหมาะสมอาจเป็นไปได้ว่าจะให้ประสิทธิภาพที่ดีกว่าโซลูชันที่ใช้ในครัวเรือน

อย่างไรก็ตามฉันไม่คิดว่าในแอปพลิเคชันขนาดเล็กหรือขนาดกลางคุณจะต้องมีการนำไปใช้ที่ซับซ้อนกว่าฐานข้อมูลปกติที่กล่าวถึงในโพสต์ก่อนหน้า

แก้ไข: ด้วยคำชี้แจงของคุณดูเหมือนว่าน่าสนใจยิ่งขึ้นที่จะใช้โซลูชันที่เหมือน JCR กับเครื่องมือค้นหา ซึ่งจะทำให้โปรแกรมของคุณง่ายขึ้นในระยะยาว


5

วิธีที่ง่ายที่สุดคือการสร้างตารางแท็ก
Target_Type- ในกรณีที่คุณติดแท็กหลายตาราง
Target- กุญแจสำคัญในการบันทึกที่ถูกแท็ก
Tag - ข้อความของแท็ก

การสืบค้นข้อมูลจะเป็นอย่างไร:

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

อัปเดต
ตามความต้องการของคุณเป็นและเงื่อนไขข้อความค้นหาด้านบนจะกลายเป็นสิ่งนี้

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

ฉันขอแนะนำ @Zizzencs ครั้งที่สองว่าคุณอาจต้องการบางสิ่งที่ไม่ได้ทั้งหมด (R) DB-centric

อย่างใดฉันเชื่อว่าการใช้เขตข้อมูล nvarchar ธรรมดาเพื่อเก็บแท็กที่มีการแคช / การจัดทำดัชนีที่เหมาะสมอาจให้ผลลัพธ์ที่เร็วขึ้น แต่นั่นเป็นเพียงฉัน

ฉันใช้ระบบการแท็กโดยใช้ 3 ตารางเพื่อแสดงความสัมพันธ์แบบหลายต่อหลายก่อน (Item แท็ก ItemTags) แต่ฉันคิดว่าคุณจะต้องจัดการกับแท็กในหลาย ๆ ที่ฉันสามารถบอกคุณได้ว่ามี 3 ตารางที่ต้อง ถูกควบคุม / เคียวรีพร้อมกันตลอดเวลาจะทำให้โค้ดของคุณซับซ้อนยิ่งขึ้น

คุณอาจต้องการพิจารณาว่าความซับซ้อนที่เพิ่มเข้ามานั้นคุ้มค่าหรือไม่


0

คุณจะไม่สามารถหลีกเลี่ยงการเข้าร่วมและยังคงเป็นมาตรฐานได้

แนวทางของฉันคือการมีตารางแท็ก

 TagId (PK)| TagName (Indexed)

จากนั้นคุณมีคอลัมน์ TagXREFID ในตารางรายการของคุณ

คอลัมน์ TagXREFID นี้เป็น FK ถึงตารางที่ 3 ฉันจะเรียกมันว่า TagXREF:

 TagXrefID | ItemID | TagId

ดังนั้นในการรับแท็กทั้งหมดสำหรับรายการจะเป็นดังนี้:

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

และเพื่อให้ได้ไอเท็มทั้งหมดสำหรับแท็กฉันจะใช้สิ่งนี้:

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

หากต้องการและรวมแท็กเข้าด้วยกันคุณจะต้องแก้ไขข้อความข้างบนเล็กน้อยเพื่อเพิ่มและแท็ก. TagName = @ TagName1 และแท็ก Tag.TagName = @ TagName2 ฯลฯ ... และสร้างคิวรีแบบไดนามิก


0

สิ่งที่ฉันชอบคือมีตารางจำนวนหนึ่งที่แสดงข้อมูลดิบดังนั้นในกรณีนี้คุณจะมี

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

วิธีนี้ใช้งานได้อย่างรวดเร็วสำหรับเวลาเขียนและทำให้ทุกอย่างเป็นมาตรฐาน แต่คุณอาจทราบว่าสำหรับแต่ละแท็กคุณจะต้องเข้าร่วมตารางสองครั้งสำหรับทุกแท็กเพิ่มเติมที่คุณต้องการและดังนั้นจึงอ่านได้ช้า

วิธีการปรับปรุงการอ่านคือการสร้างตารางแคชในคำสั่งโดยการตั้งค่าขั้นตอนการจัดเก็บที่สร้างตารางใหม่ที่แสดงถึงข้อมูลในรูปแบบที่แบน ...

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

จากนั้นคุณสามารถพิจารณาความถี่ของตารางรายการที่ถูกติดแท็กที่จะต้องได้รับการปรับปรุงให้ทันสมัยอยู่เสมอหากมีการแทรกทุกครั้งจากนั้นเรียกขั้นตอนที่เก็บไว้ในเหตุการณ์แทรกเคอร์เซอร์ หากเป็นงานประจำชั่วโมงให้ตั้งค่างานรายชั่วโมงเพื่อเรียกใช้

ตอนนี้เพื่อให้ได้ข้อมูลที่ฉลาดจริงๆแล้วคุณจะต้องสร้างกระบวนงานที่เก็บไว้เพื่อรับข้อมูลจากแท็ก แทนที่จะใช้คำค้นหาซ้อนในคำสั่ง case ขนาดใหญ่คุณต้องการส่งผ่านพารามิเตอร์เดียวที่มีรายการแท็กที่คุณต้องการเลือกจากฐานข้อมูลและส่งคืนชุดระเบียนของรายการ นี่จะเป็นรูปแบบไบนารีที่ดีที่สุดโดยใช้ตัวดำเนินการระดับบิต

ในรูปแบบไบนารีมันง่ายที่จะอธิบาย สมมติว่ามีสี่แท็กที่จะกำหนดให้กับรายการในไบนารีเราสามารถเป็นตัวแทนได้

0000

หากทั้งสี่แท็กถูกกำหนดให้กับวัตถุวัตถุจะมีลักษณะเช่นนี้ ...

1111

ถ้าแค่สองคนแรก ...

1100

จากนั้นเป็นกรณีของการค้นหาค่าไบนารีด้วย 1s และศูนย์ในคอลัมน์ที่คุณต้องการ การใช้ตัวดำเนินการ Bitwise ของ SQL Server คุณสามารถตรวจสอบว่ามี 1 ในคอลัมน์แรกโดยใช้แบบสอบถามที่ง่ายมาก

ตรวจสอบการเชื่อมโยงนี้เพื่อหามากขึ้น


0

ในการถอดความสิ่งที่คนอื่นพูด: เคล็ดลับไม่ได้อยู่ในสคีมาแต่อยู่ในข้อความค้นหาแบบสอบถาม

สกีมาที่ไร้เดียงสาของเอนทิตี / ป้ายกำกับ / แท็กเป็นวิธีการที่เหมาะสม แต่อย่างที่คุณเห็นมันไม่ชัดเจนในทันทีว่าจะทำการสืบค้น AND ด้วยแท็กจำนวนมากได้อย่างไร

วิธีที่ดีที่สุดในการเพิ่มประสิทธิภาพการค้นหานั้นจะขึ้นอยู่กับแพลตฟอร์มดังนั้นฉันขอแนะนำให้ติดแท็กคำถามของคุณใหม่ด้วย RDBS ของคุณและเปลี่ยนชื่อเรื่องเป็น "วิธีที่เหมาะสมที่สุดในการดำเนินการและสอบถามบนฐานข้อมูลการติดแท็ก"

ฉันมีคำแนะนำเล็กน้อยสำหรับ MS SQL แต่จะงดเว้นในกรณีที่ไม่ใช่แพลตฟอร์มที่คุณใช้


6
คุณอาจไม่ควรงดการให้ความรู้เกี่ยวกับเทคโนโลยีบางอย่างเพราะคนอื่นที่พยายามทำงานในโดเมนปัญหานี้อาจใช้เทคโนโลยีนั้นจริง ๆ และจะได้รับประโยชน์
Bryan Rehbein

0

การแปรผันของคำตอบข้างต้นคือใช้รหัส id จัดเรียงรวมกันเป็นสตริงที่คั่นด้วย ^ และแฮชพวกเขา จากนั้นเพียงเชื่อมโยงแฮชกับรายการ การรวมแท็กแต่ละชุดจะสร้างรหัสใหม่ หากต้องการค้นหาและเพียงแค่สร้างแฮชใหม่ด้วยรหัสแท็กและการค้นหาที่กำหนด การเปลี่ยนแท็กในรายการจะทำให้แฮชถูกสร้างขึ้นมาใหม่ รายการที่มีแท็กชุดเดียวกันจะใช้รหัสแฮชเดียวกัน


4
ด้วยวิธีการนี้คุณสามารถค้นหารายการที่มีแท็กชุดเดียวกันเท่านั้นซึ่งเป็นเรื่องเล็กน้อย ในคำถามเดิมของฉันฉันต้องการค้นหารายการที่มีแท็กทั้งหมดที่ฉันค้นหาและอาจมากกว่า
คริสเตียนเบิร์ก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.