Thiết kế cơ sở dữ liệu SQL được đề xuất cho các thẻ hoặc gắn thẻ [đã đóng]


288

Tôi đã nghe nói về một số cách để thực hiện gắn thẻ; sử dụng bảng ánh xạ giữa TagID và ItemID (có ý nghĩa với tôi, nhưng nó có chia tỷ lệ không?), thêm một số cột TagID cố định vào ItemID (có vẻ như là một ý tưởng tồi), Giữ các thẻ trong cột văn bản được phân tách bằng dấu phẩy (âm thanh điên nhưng có thể làm việc). Tôi thậm chí đã nghe ai đó đề xuất một ma trận thưa thớt, nhưng sau đó làm thế nào để tên thẻ phát triển một cách duyên dáng?

Tôi có thiếu một thực hành tốt nhất cho các thẻ?


9
Được rồi, đây là câu hỏi # 20856, câu hỏi (gần như) là # 48475 được hỏi ít nhất hai tuần sau khi câu hỏi này được hỏi.
dlamblin

9
Một câu hỏi thú vị khác là "Làm thế nào SO thực hiện các thẻ?"
Mostafa

1
Một câu hỏi thú vị khác là "Bạn sẽ quốc tế hóa chúng, và nếu vậy, làm thế nào?"
DanMan

1
So sánh thú vị (cụ thể của Postgres): cơ sở dữ
liệu.com / 2015/01 / tag

Câu trả lời:


406

Ba bảng (một để lưu trữ tất cả các mục, một cho tất cả các thẻ và một cho mối quan hệ giữa hai mục), được lập chỉ mục chính xác, với các khóa ngoại được đặt trên cơ sở dữ liệu phù hợp, nên hoạt động tốt và chia tỷ lệ đúng.

Table: Item
Columns: ItemID, Title, Content

Table: Tag
Columns: TagID, Title

Table: ItemTag
Columns: ItemID, TagID

32
Đây được gọi là giải pháp của Toxi, bạn có thể tìm thêm thông tin về nó tại đây: howto.philippkeller.com/2005/04/24/Tags-Database-schemas
Nhà phát triển Pixel

16
Một điều không được hiển thị ở đây là "thẻ" phân cấp hoặc danh mục trong bảng Thẻ. Điều này thường cần thiết trên các trang web có danh mục và danh mục con nhưng cần sự linh hoạt của việc gắn thẻ. Ví dụ: trang web công thức, trang web phụ tùng ô tô, thư mục kinh doanh, v.v. Những loại dữ liệu này thường không chỉ phù hợp với một danh mục duy nhất vì vậy gắn thẻ là câu trả lời nhưng bạn cần sử dụng một cái gì đó như Mô hình danh sách lồng nhau hoặc Mô hình danh sách điều chỉnh trong bảng Tag của bạn.
HK1

5
Tôi đồng ý với HK1 là có thể với cấu trúc trên + Bảng: Cột Taggroup: TagGropuId, Bảng tiêu đề: Cột thẻ: TagID, Title, TaggroupId
Thunder

Khi tôi muốn thêm cột css vào bảng, tôi sẽ thêm cột css vào bảng thẻ?
Amitābha

10
@ftvs: liên kết lại bị hỏng, liên kết mới là howto.philippkeller.com/2005/04/24/Tags-Database-schemas
hansaplast

83

Thông thường tôi sẽ đồng ý với Yaakov Ellis nhưng trong trường hợp đặc biệt này có một giải pháp khả thi khác:

Sử dụng hai bảng:

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

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

Điều này có một số lợi thế lớn:

Đầu tiên, nó làm cho việc phát triển đơn giản hơn nhiều: trong giải pháp ba bảng để chèn và cập nhật, itembạn phải tra cứu Tagbảng để xem đã có mục nào chưa. Sau đó, bạn phải tham gia với họ với những cái mới. Đây không phải là nhiệm vụ tầm thường.

Sau đó, nó làm cho các truy vấn đơn giản hơn (và có lẽ nhanh hơn). Có ba truy vấn cơ sở dữ liệu chính mà bạn sẽ thực hiện: Xuất tất cả Tagscho một Item, vẽ Thẻ đám mây và chọn tất cả các mục cho một Tiêu đề thẻ.

Tất cả các thẻ cho một mục:

3 bàn:

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

Bảng 2:

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

Thẻ đám mây:

3 bàn:

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

Bảng 2:

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

Các mục cho một Thẻ:

3 bàn:

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

Bảng 2:

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

Nhưng cũng có một số nhược điểm: Nó có thể chiếm nhiều không gian hơn trong cơ sở dữ liệu (điều này có thể dẫn đến nhiều hoạt động đĩa chậm hơn) và nó không được chuẩn hóa có thể dẫn đến sự không nhất quán.

Đối số kích thước không mạnh vì bản chất của thẻ là chúng thường khá nhỏ nên mức tăng kích thước không phải là lớn. Người ta có thể lập luận rằng truy vấn cho tiêu đề thẻ nhanh hơn nhiều trong một bảng nhỏ chỉ chứa mỗi thẻ một lần và điều này chắc chắn là đúng. Nhưng có liên quan đến khoản tiết kiệm cho việc không phải tham gia và thực tế là bạn có thể xây dựng một chỉ số tốt trên chúng có thể dễ dàng bù đắp cho điều này. Điều này tất nhiên phụ thuộc rất nhiều vào kích thước của cơ sở dữ liệu bạn đang sử dụng.

Đối số không nhất quán là một chút tranh luận quá. Thẻ là các trường văn bản miễn phí và không có hoạt động dự kiến ​​như 'đổi tên tất cả các thẻ "foo" thành "bar"'.

Vì vậy, tldr: Tôi sẽ đi cho giải pháp hai bàn. (Trong thực tế tôi sẽ đến. Tôi tìm thấy bài viết này để xem liệu có đối số hợp lệ chống lại nó không.)


"Index: ItemId, Title" có nghĩa là một chỉ mục cho mỗi hoặc một chỉ mục chứa cả hai?
DanMan

Thông thường hai chỉ số. Có thể phụ thuộc vào cơ sở dữ liệu bạn đang sử dụng, mặc dù.
Scheintod

1
Trong bảng thẻ là ItemId và Tag một khóa tổng hợp? hoặc bạn có PK không?
Rippo

2
bằng cách này, bạn không thể tạo các thẻ "không sử dụng" để tính năng "thêm thẻ" phải được thực hiện trên một Mục. Mặt khác, tính năng "thêm thẻ" có thể được thực hiện độc lập
Gianluca Ghettini

1
@Quangang. Tôi vẫn tin rằng nó phụ thuộc vào những gì bạn định làm :) Tôi đã thực hiện nó theo cả hai cách trong các dự án khác nhau. Trong lần cuối cùng, tôi đã kết thúc với giải pháp 3 bảng vì tôi cần "loại thẻ" (hoặc một số thông tin meta khác trên thẻ) và có thể sử dụng lại một số mã từ người anh em thân thiết của thẻ: tham số. Nhưng trong cùng một dự án, tôi đã sử dụng chính xác phương pháp này cho một người anh em họ thậm chí gần gũi hơn: cờ (ví dụ: 'đã bán', 'mới', 'nóng')
Scheintod

38

Nếu bạn đang sử dụng cơ sở dữ liệu hỗ trợ thu nhỏ bản đồ, như couchdb, lưu trữ thẻ trong trường văn bản hoặc trường danh sách thực sự là cách tốt nhất. Thí dụ:

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

Chạy cái này với group = true sẽ nhóm kết quả theo tên thẻ và thậm chí trả về số lần gặp phải thẻ đó. Nó rất giống với việc đếm sự xuất hiện của một từ trong văn bản .


4
+1 Rất vui khi thấy một số triển khai NoQuery.
Xeoncross

@NickRetallack Liên kết không hoạt động. Nếu bạn có thể, xin vui lòng cập nhật câu trả lời này.
xralf

Ok tôi đã thay thế liên kết bằng một để lưu trữ.org
Nick Retallack

13

Sử dụng một cột văn bản được định dạng duy nhất [1] để lưu trữ các thẻ và sử dụng công cụ tìm kiếm toàn văn có khả năng để lập chỉ mục này. Khác, bạn sẽ gặp vấn đề mở rộng khi cố gắng thực hiện các truy vấn boolean.

Nếu bạn cần chi tiết về các thẻ bạn có, bạn có thể theo dõi nó trong bảng được duy trì tăng dần hoặc chạy một công việc hàng loạt để trích xuất thông tin.

[1] Một số RDBMS thậm chí còn cung cấp một kiểu mảng gốc có thể phù hợp hơn cho việc lưu trữ bằng cách không cần một bước phân tích cú pháp, nhưng có thể gây ra vấn đề với tìm kiếm toàn văn bản.


Bạn có biết về bất kỳ công cụ tìm kiếm toàn văn bản nào không tìm thấy các biến thể của một từ không? Ví dụ, tìm kiếm sách trả về sách? Ngoài ra, bạn làm gì về các thẻ như "c ++"? SQL Server, ví dụ, sẽ loại bỏ các dấu cộng trong chỉ mục. Cảm ơn.
Jonathan Wood

Hãy thử Sphinx - sphinxsearch.com
Roman

Hướng dẫn gồm 3 phần này có thể hữu ích cho những ai đang đi theo lộ trình này (tìm kiếm toàn văn). Nó đang sử dụng các tiện ích gốc PostgreSQL: shisaa.jp/postset/postgresql-full-text-search-part-1.html
Sẽ

điều này có tốt hơn câu trả lời được chọn về mặt hiệu suất không?

Làm thế nào về việc lưu trữ trong việc sử dụng varchar 255, các thẻ được phân tách bằng dấu phẩy và thêm chỉ mục văn bản kfull trên đó?

9

Tôi đã luôn giữ các thẻ trong một bảng riêng biệt và sau đó có một bảng ánh xạ. Tất nhiên tôi cũng chưa bao giờ làm bất cứ điều gì ở quy mô lớn.

Việc có bảng "thẻ" và bảng bản đồ giúp tạo ra các đám mây thẻ khá đơn giản và vì vậy bạn có thể dễ dàng kết hợp SQL để có được danh sách các thẻ với số lần sử dụng mỗi thẻ.


6
Điều này thậm chí còn dễ dàng hơn nếu bạn không sử dụng bảng ánh xạ :)
Scheintod

0

Tôi muốn đề xuất thiết kế sau: Bảng mục: Itemid, taglist1, taglist2
điều này sẽ nhanh chóng và giúp dễ dàng lưu và truy xuất dữ liệu ở cấp độ mục.

Song song xây dựng một bảng khác: Thẻ thẻ không tạo định danh duy nhất cho thẻ và nếu bạn hết dung lượng trong cột thứ 2 có chứa 100 mục sẽ tạo một hàng khác.

Bây giờ trong khi tìm kiếm các mục cho một thẻ, nó sẽ siêu nhanh.


vi.wikipedia.org/wiki/First_n normal_form mặc dù có những ngoại lệ đối với điều này, bạn có thể không chuẩn hóa, nhưng không phải ở đây
Dheeraj
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.