Làm thế nào tôi nên thiết kế một bảng mối quan hệ cho tình bạn?


33

Nếu Alà một người bạn của tôi B, thì tôi nên lưu trữ cả hai giá trị ABBA, hoặc một giá trị là đủ? Những lợi thế và bất lợi của cả hai phương pháp là gì.

Đây là quan sát của tôi:

  • Nếu tôi giữ cả hai thì tôi phải cập nhật cả hai khi nhận được yêu cầu từ một người bạn.
  • Nếu tôi không giữ cả hai, thì tôi thấy khó khăn khi phải thực hiện nhiều lần JOINvới bảng này.

Hiện tại, tôi giữ mối quan hệ một chiều.

nhập mô tả hình ảnh ở đây

Vậy tôi nên làm gì trong trường hợp này? Có lời khuyên nào không?


Bạn có cam kết với một nền tảng, hay đây là một câu hỏi lý thuyết?
Nick Chammas

Điều gì về một cách tiếp cận hỗn hợp: mô hình tình bạn được yêu cầu và không được đáp ứng tương ứng trong các bảng riêng biệt, đảm bảo một tình bạn được chèn vào chính xác một trong những bảng đó, không hay để đạt được bằng cách sử dụng các sản phẩm SQL ngày nay :(
onedaywhen

@encedaywhen - Vâng, âm thanh phù hợp hơn cho cơ sở dữ liệu đồ thị .
Nick Chammas

@NickChammas: Đây không phải là câu hỏi lý thuyết. Tôi đang làm việc trên mysqlđó được lưu trữ trên đám mây Amazon.
Chan

1
@Chan - Ah có nghĩa là bạn không thể sử dụng các ràng buộc kiểm tra để thực thi mối quan hệ chỉ được lưu trữ theo một cách sau đó (MySQL không thực thi những điều này)
Martin Smith

Câu trả lời:


30

Tôi sẽ lưu trữ AB và BA. Một tình bạn thực sự là một mối quan hệ hai chiều, mỗi thực thể được liên kết với nhau. Mặc dù theo trực giác, chúng tôi nghĩ rằng "tình bạn" là một liên kết giữa hai người, từ quan điểm quan hệ, nó giống như "A có một người bạn B" và "B có một người bạn A". Hai mối quan hệ, hai hồ sơ.


3
Cảm ơn nhiều. Tôi thực sự cần phải suy nghĩ về ý tưởng của bạn một cách cẩn thận! Lý do tôi tránh lưu trữ AB và BA là vì lưu trữ, vì mỗi lần tôi có một tình bạn, bảng của tôi sẽ lưu trữ gấp đôi.
Chan

1
Bạn nói đúng về lưu trữ, nhưng hãy nhớ rằng nếu được lưu dưới dạng số nguyên, mỗi mối quan hệ bạn bè sẽ mất khoảng 30 byes (2 bản ghi x 3 cột x 4 byte cho mỗi số nguyên = 24 byte cộng với một số phần đệm). Mỗi người có 1 triệu người với 10 người bạn sẽ chỉ còn khoảng 300 MB dữ liệu.
datagod

1
datagod: đúng vậy!
Chan

Đây là cách tôi thiết kế các bảng của mình, AB & BA.
kabuto178

2
Ngoài ra, trong các tình huống chỉ có AB và không có BA, điều này có thể thể hiện 'yêu cầu kết bạn đang chờ xử lý'.
Greg

13

Nếu tình bạn được dự định là đối xứng (nghĩa là không thể A kết bạn Bnhưng không phải ngược lại) thì tôi sẽ chỉ lưu trữ mối quan hệ một chiều với một ràng buộc kiểm tra để đảm bảo rằng mỗi mối quan hệ chỉ có thể được biểu diễn theo một cách.

Ngoài ra, tôi sẽ bỏ id thay thế và có PK tổng hợp thay thế (và có thể là một chỉ số duy nhất tổng hợp cũng trên các cột đảo ngược).

CREATE TABLE Friends
  (
     UserID1 INT NOT NULL REFERENCES Users(UserID),
     UserID2 INT NOT NULL REFERENCES Users(UserID),
     CONSTRAINT CheckOneWay CHECK (UserID1 < UserID2),
     CONSTRAINT PK_Friends_UserID1_UserID2 PRIMARY KEY (UserID1, UserID2),
     CONSTRAINT UQ_Friends_UserID2_UserID1 UNIQUE (UserID2, UserID1)
  ) 

Bạn không nói các truy vấn gây khó khăn nhưng bạn luôn có thể tạo Chế độ xem

CREATE VIEW Foo
AS
SELECT UserID1,UserID2 
FROM Friends
UNION ALL
SELECT UserID2,UserID1 
FROM Friends

Tôi biết cái này khá cũ, nên xin lỗi vì đã đào cái này lên. Sẽ không tốt hơn nếu KHÔNG xác định chỉ số tình bạn đảo ngượcUNIQUE , để không đặt thêm gánh nặng không cần thiết và dư thừa lên INSERTs? Vì chúng ta có PRIMARY KEY (a,b)và kể từ khi có PK UNIQUE, việc đảo ngược KEY (b,a)cũng UNIQUEkhông có vấn đề gì.
tfrommen

1
@tf Đoán điều đó phụ thuộc vào trình tối ưu hóa querŷ. Như bạn chỉ ra, chỉ cần kiểm tra một chiều để kế hoạch chèn có thể thực hiện việc này bằng mọi cách. Câu hỏi được gắn thẻ MySQL - không biết hành vi đó như thế nào.
Martin Smith

Tôi biết đây là một câu trả lời cũ, nhưng tôi chỉ muốn chỉ ra cho bất kỳ ai vấp phải điều này rằng MySQL hoàn toàn bỏ qua các ràng buộc CHECK (mặc dù nó sẽ "phân tích" chúng thành công) vì vậy cách tiếp cận này có lẽ không phải là cách để đi với công nghệ đó.
Mi-chê

@Micah đúng. Tôi đã không biết điều đó vào năm 2012. Vẫn sẽ hoạt động trong các DBMS khác ...
Martin Smith

+1 để thực hiện Chế độ xem cho điều đó. Việc lưu trữ AB & BA mang lại sự không nhất quán (nếu mối quan hệ không phải là hai chiều) trong khi phương pháp này là một cách tiếp cận tốt hơn
imans77

7

Giả sử một "tình bạn" luôn là hai chiều / lẫn nhau, có lẽ tôi sẽ xử lý nó như thế này.

CREATE TABLE person (
    person_id int IDENTITY(1,1) PRIMARY KEY,
    ...other columns...
)

CREATE TABLE friendship (
    friendship_id int IDENTITY(1,1) PRIMARY KEY,
    ...other columns, if any...
)

CREATE TABLE person_friendship (
    person_id int NOT NULL,
    friendship_id int NOT NULL
    PRIMARY KEY (person_id, friendship_id)
)

Kết quả là bạn thay đổi nó từ một người tham gia nhiều người từ "người" thành "người", thành người tham gia nhiều người từ "người" sang "tình bạn". Điều này sẽ đơn giản hóa việc tham gia và các ràng buộc, nhưng có tác dụng phụ là cho phép nhiều hơn hai người trong một "tình bạn" (mặc dù có thể sự linh hoạt bổ sung sẽ là một lợi thế tiềm năng).


Đây về cơ bản là một mô hình nhóm / thành viên. Ý tưởng thú vị, mặc dù.
einSelbst

4

Bạn có thể cần xác định các chỉ mục xung quanh mối quan hệ bạn bè thay vì nhân đôi số lượng hàng:

CREATE TABLE person
(
    person_id INT NOT NULL AUTO_INCREMENT,
    ...
    PRIMARY KEY (person_id)
);
CREATE TABLE friendship
(
    friend_of INT NOT NULL,
    friend_to INT NOT NULL,
    PRIMARY KEY (friend_of,friend_to),
    UNIQUE KEY friend_to (friend_to,friend_of)
);

Bằng cách này, bạn tăng gấp đôi dung lượng lưu trữ cho các chỉ mục nhưng không phải cho dữ liệu bảng. Do đó, đây sẽ là một khoản tiết kiệm 25% trên không gian đĩa. Trình tối ưu hóa truy vấn MySQL sẽ chọn chỉ thực hiện quét phạm vi chỉ mục, đó là lý do tại sao khái niệm bao phủ các chỉ mục hoạt động tốt ở đây.

Dưới đây là một số liên kết đẹp về Chỉ số che phủ:

CẨN THẬN

Nếu tình bạn không có nhau, bạn có cơ sở cho một loại mối quan hệ khác: SAU

Nếu friend_to không phải là bạn của friend_of, bạn có thể bỏ mối quan hệ đó ra khỏi bàn.

Nếu bạn muốn xác định mối quan hệ cho tất cả các loại, cho dù chúng có tương hỗ hay không, bạn có thể sử dụng cách bố trí bảng sau:

CREATE TABLE person
(
    person_id INT NOT NULL AUTO_INCREMENT,
    ...
    PRIMARY KEY (person_id)
);
CREATE TABLE relationship
(
    rel_id INT NOT NULL AUTO_INCREMENT,
    person_id1 INT NOT NULL,
    person_id2 INT NOT NULL,
    reltype_id TINYINT,
    PRIMARY KEY (rel_id),
    UNIQUE KEY outer_affinity (reltype_id,person_id1,person_id2),
    UNIQUE KEY inner_affinity (reltype_id,person_id2,person_id1),
    KEY has_relationship_to (person1_id,reltype_id),
    KEY has_relationship_by (person2_id,reltype_id)
);
CREATE TABLE relation
(
    reltype_id TINYINT NOT NULL AUTO_INCREMENT,
    rel_name VARCHAR(20),
    PRIMARY KEY (reltype_id),
    UNIQUE KEY (rel_name)
);
INSERT INTO relation (relation_name) VALUES
('friend'),('follower'),('foe'),
('forgotabout'),('forsaken'),('fixed');

Từ bảng quan hệ, bạn có thể sắp xếp các mối quan hệ để bao gồm:

  • Bạn bè nên tương thân
  • Kẻ thù có thể là tương hỗ hoặc không
  • Người theo dõi có thể tương hỗ hoặc không
  • Các mối quan hệ khác sẽ chịu sự giải thích (bởi sự lãng quên hoặc bị bỏ rơi hoặc người nhận trả thù (đã sửa))
  • Mối quan hệ có thể được mở rộng hơn nữa

Điều này nên mạnh mẽ hơn cho tất cả các mối quan hệ, cho dù mối quan hệ là tương hỗ hay không.


xin chào @rolandomysqldba, tôi rất hâm mộ câu trả lời của bạn. nó thực sự hữu ích với tôi (trong trường hợp này là ví dụ thứ 1). Bây giờ đây là một cảnh báo cho tôi, tôi muốn mối quan hệ độc đáo. (ví dụ: Nếu người dùng A kết bạn với B thì bạn B không thể chấp nhận được.) Tôi có nên thực hiện với trình kích hoạt không? và hiệu suất thì sao? bởi vì tôi có bảng rất lớn (khoảng 1 triệu bản ghi) và Nếu tôi tìm kiếm bạn bè của Người dùng A (A được lưu trữ trong cả hai trường (friend_of, friend_to) và mysql chỉ sử dụng một chỉ mục, thì nó hoạt động rất chậm. Tôi phải lưu trữ các mục trùng lặp trong bảng của mình (ví dụ: A-> B, B-> A). Có lựa chọn nào tốt hơn không?
Manish Sapkal

1

Nếu bạn có thể kiểm soát trong ứng dụng, id của A luôn thấp hơn id của B (đặt trước các id phần tử A, B), bạn có thể tận dụng yêu cầu mà không cần OR (chọn nơi id_A = a AND id_B = b, thay vì hỏi (id_A = a AND id_B = b) HOẶC (id_A = b AND id_B = a)) và cũng duy trì một nửa số hồ sơ bạn sẽ cần với các xấp xỉ của người khác. Sau đó, bạn nên sử dụng một lĩnh vực khác để duy trì trạng thái của mối quan hệ (là bạn bè, a-solicited-to-b, b-solicited-to-a, exfriends-a, exfriends-b), và bạn đã hoàn thành.

Đây là cách tôi đã quản lý hệ thống tình bạn của mình và điều này giúp đơn giản hóa hệ thống và sử dụng một nửa số hàng bạn sẽ cần với các hệ thống khác, chỉ nói A bằng với giá trị id thấp hơn trong mã.

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.