Bagaimana saya harus merancang tabel hubungan untuk pertemanan?


33

Jika Ateman B, maka haruskah saya menyimpan nilai ABdan BA, atau satu sudah cukup? Apa kelebihan dan kekurangan kedua metode tersebut.

Inilah pengamatan saya:

  • Jika saya menyimpan keduanya maka saya harus memperbarui keduanya ketika menerima permintaan dari seorang teman.
  • Jika saya tidak menyimpan keduanya, maka saya merasa kesulitan ketika harus melakukan beberapa JOINdengan tabel ini.

Saat ini, saya menjaga hubungan tetap satu arah.

masukkan deskripsi gambar di sini

Jadi apa yang harus saya lakukan dalam kasus ini? Ada saran?


Apakah Anda berkomitmen pada platform, atau ini pertanyaan teoretis?
Nick Chammas

Bagaimana dengan pendekatan hybrid: model persahabatan yang dibalas dan tidak berbalas masing-masing dalam tabel terpisah, memastikan persahabatan dimasukkan ke dalam salah satu dari tabel tersebut, tidak baik untuk dicapai menggunakan produk SQL hari ini :(
onedaywhen

@onedaywhen - Ya, terdengar lebih cocok untuk basis data grafik .
Nick Chammas

@NickChammas: Ini bukan pertanyaan teoretis. Saya sedang mengerjakan mysqlyang disimpan di cloud Amazon.
Chan

1
@Chan - Ah itu berarti Anda tidak dapat menggunakan cek kendala untuk menegakkan hubungan hanya disimpan satu arah (MySQL tidak menegakkan ini)
Martin Smith

Jawaban:


30

Saya akan menyimpan AB dan BA. Persahabatan sebenarnya adalah hubungan dua arah, setiap entitas terhubung dengan yang lain. Meskipun secara intuitif kita menganggap "persahabatan" sebagai satu tautan antara dua orang, dari sudut pandang relasional lebih seperti "A memiliki teman B" dan "B memiliki teman A". Dua hubungan, dua catatan.


3
Terimakasih banyak. Saya benar-benar perlu memikirkan ide Anda dengan hati-hati! Alasan saya menghindari menyimpan AB dan BA adalah karena penyimpanan, karena setiap kali saya memiliki persahabatan, meja saya akan menyimpan dua kali lipat.
Chan

1
Anda benar tentang penyimpanan, tetapi ingat bahwa jika disimpan sebagai bilangan bulat, setiap hubungan teman-teman akan memakan waktu sekitar 30 bye (2 catatan x 3 kolom x 4 byte per integer = 24 byte plus beberapa lapisan). 1 juta orang dengan 10 teman masing-masing masih hanya sekitar 300MB data.
datagod

1
datagod: benar!
Chan

Ini adalah bagaimana saya mendesain meja saya juga, AB & BA.
kabuto178

2
Plus, dalam situasi di mana hanya ada AB dan bukan BA, ini dapat mewakili 'permintaan teman tertunda'.
Greg

13

Jika persahabatan dimaksudkan untuk menjadi simetris (yaitu tidak mungkin untuk A berteman dengan Btetapi tidak sebaliknya) maka saya hanya akan menyimpan hubungan satu arah dengan batasan cek memastikan bahwa setiap hubungan hanya dapat diwakili satu arah.

Juga saya akan membuang id pengganti dan memiliki PK komposit sebagai gantinya (dan mungkin indeks unik komposit juga pada kolom terbalik).

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

Anda tidak mengatakan pertanyaan yang menyulitkan ini tetapi Anda selalu dapat membuat Tampilan

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

Saya tahu ini sudah cukup tua, maaf sudah menggali ini. Bukankah lebih baik untuk TIDAK mendefinisikan indeks persahabatan terbalikUNIQUE , agar tidak menempatkan beban tambahan yang tidak perlu dan berlebihan INSERT? Karena kita memiliki PRIMARY KEY (a,b)dan karena PK adalah UNIQUE, yang terbalik KEY (b,a)juga UNIQUEtidak peduli apa.
tfrommen

1
@ tf Tebak itu tergantung pada optimizer kuerŷ. Seperti yang Anda tunjukkan, Anda hanya perlu memeriksa satu arah sehingga rencana penyisipan mungkin akan melakukan ini. Pertanyaan ini ditandai dengan MySQL - tidak tahu bagaimana itu berlaku.
Martin Smith

Saya tahu ini adalah jawaban lama, tetapi saya hanya ingin menunjukkan kepada siapa pun yang tersandung pada hal ini bahwa MySQL sepenuhnya mengabaikan kendala CHECK (meskipun itu akan "mengurai" mereka dengan sukses) sehingga pendekatan ini mungkin bukan cara untuk pergi dengan teknologi itu.
Mikha

@Micah benar. Saya tidak menyadarinya pada tahun 2012. Masih akan bekerja di DBMS lainnya ...
Martin Smith

+1 untuk menerapkan Tampilan untuk itu. Memiliki AB & BA disimpan membawa inkonsistensi (jika hubungan ini tidak bi-directional) sedangkan metode ini adalah pendekatan yang lebih baik
imans77

7

Dengan asumsi "persahabatan" selalu dua arah / saling, saya mungkin akan menanganinya seperti ini.

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

Hasilnya adalah Anda mengubahnya dari banyak-ke-banyak bergabung dari "orang" menjadi "orang", menjadi banyak-ke-banyak bergabung dari "orang" menjadi "persahabatan". Ini akan menyederhanakan gabungan dan kendala, tetapi memiliki efek samping yang memungkinkan lebih dari dua orang dalam "persahabatan" tunggal (meskipun mungkin fleksibilitas tambahan akan menjadi keuntungan potensial).


Ini pada dasarnya adalah pola grup / keanggotaan. Ide yang menarik.
einSelbst

4

Anda mungkin perlu menentukan indeks di sekitar pertemanan alih-alih menggandakan jumlah baris:

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)
);

Dengan cara ini, Anda menggandakan penyimpanan untuk indeks tetapi tidak untuk data tabel. Akibatnya, ini harus menjadi penghematan 25% pada ruang disk. MySQL Query Optimizer akan memilih melakukan pemindaian rentang indeks saja, itulah sebabnya mengapa konsep yang mencakup indeks berfungsi dengan baik di sini.

Berikut ini beberapa tautan bagus di Covering Indexes:

CAVEAT

Jika persahabatan tidak saling menguntungkan, Anda memiliki dasar untuk jenis hubungan lain: FOLLOWER

Jika friend_to bukan teman friend_of, Anda bisa mengabaikan hubungan itu.

Jika Anda ingin mendefinisikan hubungan untuk semua jenis, apakah itu saling atau tidak, Anda mungkin bisa menggunakan tata letak tabel berikut:

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');

Dari tabel relasi, Anda dapat mengatur hubungan untuk menyertakan yang berikut:

  • Teman harus saling menguntungkan
  • Musuh bisa saling menguntungkan atau tidak
  • Pengikut bisa saling atau tidak
  • Hubungan lainnya akan tunduk pada interpretasi (oleh yang dilupakan atau ditinggalkan atau penerima balas dendam (tetap))
  • Kemungkinan hubungan bisa diperpanjang

Ini harus lebih kuat untuk semua hubungan, apakah hubungan itu timbal balik atau tidak.


hai @rolandomysqldba, saya penggemar jawaban Anda. itu sangat membantu saya (dalam hal ini contoh 1). Sekarang di sini adalah satu peringatan bagi saya, saya ingin hubungan yang unik. (mis. Jika pengguna A berteman dengan B maka, teman B dengan A tidak dapat diterima.) haruskah saya lakukan dengan pemicu? dan bagaimana dengan kinerja? karena saya memiliki tabel yang sangat besar (sekitar 1 juta catatan), dan Jika saya mencari teman Pengguna A (A disimpan di bidang (friend_of, friend_to), dan mysql hanya menggunakan satu indeks, maka kinerjanya sangat lambat. Saya harus menyimpan entri duplikat di meja saya (mis. A-> B, B-> A) .Apakah opsi yang lebih baik?
Manish Sapkal

1

Jika Anda dapat mengontrol dalam aplikasi bahwa id A selalu lebih rendah dari id B (pre order A, elemen B id) Anda dapat memanfaatkan bertanya tanpa OR (pilih di mana id_A = a DAN id_B = b, alih-alih bertanya (id_A = a AND id_B = b) ATAU (id_A = b AND id_B = a)), dan juga memelihara setengah dari catatan yang Anda perlukan dengan perkiraan pihak lain. Maka Anda harus menggunakan bidang lain untuk mempertahankan status hubungan (adalah-teman, a-solicited-to-b, b-solicited-to-a, exfriends-a, exfriends-b), dan Anda selesai.

Ini adalah cara saya mengelola sistem pertemanan saya, dan ini menyederhanakan sistem dan menggunakan setengah baris yang Anda perlukan dengan sistem lain, hanya mengatakan A sama dengan nilai id yang lebih rendah dalam kode.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.