Desain database SQL yang disarankan untuk tag atau penandaan [ditutup]

288

Saya telah mendengar beberapa cara untuk menerapkan penandaan; menggunakan tabel pemetaan antara TagID dan ItemID (masuk akal bagi saya, tetapi apakah ini skala?), menambahkan sejumlah kolom TagID yang mungkin ke ItemID (sepertinya ide yang buruk), Menyimpan tag dalam kolom teks yang dipisahkan koma (terdengar gila tapi bisa bekerja). Saya bahkan pernah mendengar seseorang merekomendasikan matriks jarang, tetapi kemudian bagaimana nama tag tumbuh dengan anggun?

Apakah saya melewatkan praktik terbaik untuk tag?

dlamblin
sumber
9
Oke ini pertanyaan # 20856, pertanyaan (hampir) yang sama adalah # 48475 ditanyakan setidaknya dua minggu setelah pertanyaan ini diajukan.
dlamblin
9
Pertanyaan menarik lainnya adalah "Bagaimana SO mengimplementasikan tag?"
Mostafa
1
Pertanyaan menarik lainnya adalah "Apakah Anda akan menginternasionalkan mereka, dan jika demikian, bagaimana?"
DanMan
1
Perbandingan menarik (spesifik Postgres): databasesoup.com/2015/01/tag-all-things.html
a_horse_with_no_name

Jawaban:

406

Tiga tabel (satu untuk menyimpan semua item, satu untuk semua tag, dan satu untuk hubungan antara keduanya), diindeks dengan benar, dengan kunci asing diatur berjalan pada database yang tepat, harus bekerja dengan baik dan skala dengan benar.

Table: Item
Columns: ItemID, Title, Content

Table: Tag
Columns: TagID, Title

Table: ItemTag
Columns: ItemID, TagID
Yaakov Ellis
sumber
32
Ini dikenal sebagai solusi "Toxi", Anda dapat menemukan informasi tambahan tentang hal ini di sini: howto.philippkeller.com/2005/04/24/Tags-Database-schemas
The Pixel Developer
16
Satu hal yang tidak ditampilkan di sini adalah "tag" hierarkis atau kategori dalam tabel Tag. Ini biasanya diperlukan di situs yang memiliki kategori dan subkategori tetapi membutuhkan fleksibilitas pemberian tag. Misalnya, situs resep, situs onderdil mobil, direktori bisnis, dll. Jenis data ini biasanya tidak masuk ke dalam satu kategori tunggal sehingga penandaan adalah jawabannya tetapi Anda perlu menggunakan sesuatu seperti Nested Set Model atau Adjacency List Model di tabel Tag Anda.
HK1
5
Saya setuju dengan HK1 apakah mungkin dengan struktur di atas + Tabel: TagGroup Kolom: TagGropuId, Judul Tabel: Tag Kolom: TagID, Judul, TagGroupId
Guntur
ketika saya ingin menambahkan kolom css ke tabel, saya akan menambahkan kolom css ke dalam tabel tag?
Amitābha
10
@ftvs: tautan lagi putus, tautan baru adalah howto.philippkeller.com/2005/04/24/Tags-Database-schemas
hansaplast
83

Biasanya saya setuju dengan Yaakov Ellis tetapi dalam kasus khusus ini ada solusi lain yang layak:

Gunakan dua tabel:

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

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

Ini memiliki beberapa keunggulan utama:

Pertama itu membuat pengembangan lebih sederhana: dalam solusi tiga-tabel untuk memasukkan dan memperbarui itemAnda harus mencari Tagtabel untuk melihat apakah sudah ada entri. Maka Anda harus bergabung dengan mereka dengan yang baru. Ini bukan tugas sepele.

Maka itu membuat kueri lebih sederhana (dan mungkin lebih cepat) Ada tiga permintaan basis data utama yang akan Anda lakukan: Keluarkan semua Tagsuntuk satu Item, gambarkan Tag-Cloud dan pilih semua item untuk satu Judul Tag.

Semua Tag untuk satu Item:

3-Meja:

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

2-Meja:

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

Tag-Cloud:

3-Meja:

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

2-Meja:

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

Item untuk satu Tag:

3-Meja:

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

2-Meja:

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

Tetapi ada beberapa kelemahan juga: Ini bisa mengambil lebih banyak ruang dalam database (yang dapat menyebabkan lebih banyak operasi disk yang lebih lambat) dan itu tidak dinormalisasi yang dapat menyebabkan inkonsistensi.

Argumen ukuran tidak terlalu kuat karena sifat tag adalah bahwa mereka biasanya cukup kecil sehingga peningkatan ukurannya tidak besar. Orang bisa berargumen bahwa permintaan untuk judul tag jauh lebih cepat dalam sebuah tabel kecil yang berisi setiap tag hanya sekali dan ini tentunya benar. Tetapi dengan mempertimbangkan penghematan karena tidak harus bergabung dan fakta bahwa Anda dapat membangun indeks yang baik pada mereka dapat dengan mudah mengimbangi ini. Ini tentu saja sangat tergantung pada ukuran database yang Anda gunakan.

Argumen inkonsistensi juga sedikit diperdebatkan. Tag adalah bidang teks gratis dan tidak ada operasi yang diharapkan seperti 'ganti nama semua tag "foo" menjadi "bar"'.

Jadi tldr: Saya akan mencari solusi dua meja. (Sebenarnya saya akan. Saya menemukan artikel ini untuk melihat apakah ada argumen yang valid menentangnya.)

Scheintod
sumber
Apakah "Indeks: ItemId, Judul" berarti indeks untuk masing-masing atau satu indeks yang mengandung keduanya?
DanMan
Biasanya dua indeks. Namun, ini bisa bergantung pada database yang Anda gunakan.
Scheintod
1
Dalam tabel tag, apakah ItemId dan Tag merupakan kunci komposit? atau apakah Anda memiliki PK juga?
Rippo
2
dengan cara ini Anda tidak dapat membuat tag "tidak digunakan" sehingga fitur "tag menambahkan" harus dilakukan pada Item. Di metode lain, fitur "tambahkan tag" dapat dilakukan secara independen
Gianluca Ghettini
1
@Quilang. Saya masih percaya itu tergantung pada apa yang Anda lakukan :) Saya menerapkannya dalam kedua cara di berbagai proyek. Dalam yang terakhir saya, saya berakhir dengan solusi 3 tabel karena saya membutuhkan "tag-type" (atau informasi meta lain pada tag) dan dapat menggunakan kembali beberapa kode dari sepupu dekat tag: parameter. Tetapi dalam proyek yang sama saya menggunakan metode ini untuk sepupu yang lebih dekat: bendera (mis. 'Terjual', 'baru', 'panas')
Scheintod
38

Jika Anda menggunakan basis data yang mendukung pengurangan peta, seperti couchdb, menyimpan tag di bidang teks biasa atau bidang daftar memang cara terbaik. Contoh:

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

Menjalankan ini dengan group = true akan mengelompokkan hasil berdasarkan nama tag, dan bahkan mengembalikan hitungan berapa kali tag ditemui. Ini sangat mirip dengan menghitung kemunculan kata dalam teks .

Nick Retallack
sumber
4
+1 Senang melihat beberapa implementasi NoSQL juga.
Xeoncross
@NickRetallack Tautan tidak berfungsi. Jika Anda bisa, harap perbarui jawaban ini.
xralf
Ok saya mengganti tautannya dengan satu ke archive.org
Nick Retallack
13

Gunakan kolom teks berformat tunggal [1] untuk menyimpan tag dan gunakan mesin pencari teks lengkap yang mampu mengindeks ini. Jika tidak, Anda akan mengalami masalah penskalaan saat mencoba menerapkan kueri boolean.

Jika Anda membutuhkan detail tentang tag yang Anda miliki, Anda dapat melacaknya di tabel yang dikelola secara bertahap atau menjalankan pekerjaan batch untuk mengekstrak informasi.

[1] Beberapa RDBMS bahkan menyediakan tipe array asli yang mungkin lebih cocok untuk penyimpanan dengan tidak memerlukan langkah penguraian, tetapi mungkin menyebabkan masalah dengan pencarian teks lengkap.

David Schmitt
sumber
Apakah Anda mengetahui adanya mesin pencarian teks lengkap yang tidak menemukan variasi kata? Misalnya, mencari buku pengembalian buku? Juga, apa yang Anda lakukan tentang tag seperti "c ++"? SQL Server, misalnya, akan menghapus tanda plus dalam indeks. Terima kasih.
Jonathan Wood
Coba Sphinx - sphinxsearch.com
Roman
Tutorial 3 bagian ini mungkin berguna bagi mereka yang menggunakan rute ini (pencarian teks lengkap). Itu menggunakan fasilitas asli PostgreSQL: shisaa.jp/postset/postgresql-full-text-search-part-1.html
Will
Apakah ini lebih baik daripada jawaban yang dipilih dalam hal kinerja?
bagaimana dengan menyimpan menggunakan varchar 255, tanda dipisah koma dan menambahkan indeks teks kfull di atasnya?
9

Saya selalu menyimpan tag di tabel terpisah dan kemudian memiliki tabel pemetaan. Tentu saja saya juga tidak pernah melakukan sesuatu dalam skala yang sangat besar.

Memiliki tabel "tag" dan tabel peta membuatnya cukup sepele untuk menghasilkan tag cloud & semacamnya karena Anda dapat dengan mudah mengumpulkan SQL untuk mendapatkan daftar tag dengan jumlah seberapa sering setiap tag digunakan.

Mark Biek
sumber
6
Ini bahkan lebih mudah jika Anda tidak menggunakan tabel pemetaan :)
Scheintod
0

Saya akan menyarankan desain berikut: Tabel Item: Itemid, taglist1, taglist2
ini akan cepat dan membuat mudah menyimpan dan mengambil data pada level item.

Secara paralel buat tabel lain: Tag tag tidak membuat tag pengidentifikasi unik dan jika Anda kehabisan ruang di kolom ke-2 yang berisi katakanlah 100 item buat baris lain.

Sekarang saat mencari item untuk sebuah tag, itu akan sangat cepat.

pengguna236575
sumber
en.wikipedia.org/wiki/First_normal_form walaupun ada pengecualian untuk ini, Anda dapat melakukan denormalize, tetapi tidak di sini
Dheeraj