Arkadaşlık için nasıl bir ilişki tablosu tasarlamalıyım?


33

Eğer Abir arkadaşım B, o zaman ben her iki değeri saklamak gerekir ABve BAya bir tane yeter? Her iki yöntemin avantaj ve dezavantajları nelerdir?

İşte gözlemim:

  • İkisini de tutarsam, bir arkadaşımdan bir istek aldığımda ikisini de güncellemem gerekir.
  • Her ikisini de tutmazsam, o zaman JOINbu tabloyla çoklu yapmak zorunda kalırken zor buldum .

Şu anda ilişkiyi bir yönden sürdürüyorum.

görüntü tanımını buraya girin

Peki bu durumda ne yapmalıyım? Herhangi bir tavsiye?


Bir platforma bağlı mısın, yoksa bu teorik bir soru mu?
Nick Chammas

Hibrit bir yaklaşıma ne dersiniz: model, sırasıyla ayrı tablolarda aranan ve karşılıksız dostluklar, bir dostluğun tam olarak bu tablolardan birine girmesini sağlayın, bugünün SQL ürünlerini kullanarak elde etmek için iyi değil :(
Perşembe

@ onedaywhen - Evet, bir grafik veritabanı için daha uygun sesler .
Nick Chammas

@NickChammas: Bu teorik bir soru değil. mysqlAmazon bulutunda depolanan üzerinde çalışıyorum .
Chan

1
@Chan - Ah, ilişkiyi zorlamak için çek kısıtlamalarını kullanamayacağınız anlamına gelir, o zaman sadece bir yolla saklanır (MySQL bunları zorlamaz)
Martin Smith

Yanıtlar:


30

AB ve BA’yı saklardım. Bir arkadaşlık gerçekten iki yönlü bir ilişkidir, her varlık bir başkasıyla bağlantılıdır. Sezgisel olarak "dostluğu" iki insan arasında tek bir bağlantı olarak düşünmemize rağmen, ilişkisel açıdan "A'nın bir arkadaşı B var" ve "B'nin arkadaşı A" gibi olduğunu düşünüyorum. İki ilişki, iki kayıt.


3
Çok teşekkürler. Gerçekten fikrini dikkatlice düşünmem gerekiyor! AB ve BA'yı saklamaktan kaçınmamın nedeni depolama yüzünden, bir arkadaşlığım olduğu için masam iki kat daha fazla depolayacaktı.
Chan

1
Depolama konusunda haklısınız, ancak tamsayılar olarak saklanırsa her arkadaş-arkadaş ilişkisinin yaklaşık 30 bayt alacağını unutmayın (tamsayı başına 2 kayıt x 3 sütun x 4 bayt = 24 bayt artı bir miktar dolgu). Her biri 10 arkadaşı olan 1 milyon insan hala sadece 300 MB veri civarında olacaktı.
datagod,

1
datagod: doğru!
Chan

Tablolarımı da böyle tasarladım, AB & BA.
kabuto178

2
Ayrıca, yalnızca AB'nin olduğu ve BA'nın olmadığı durumlarda, bu 'bekleyen bir arkadaşlık isteğini' temsil edebilir.
Greg

13

Arkadaşlığın simetrik olması isteniyorsa (yani A arkadaş olması mümkün değil Bama tersi olmasa da), o zaman her ilişkinin sadece bir şekilde temsil edilebildiğinden emin olmak için bir kontrol kısıtlamasıyla tek yönlü bir ilişkiyi saklardım.

Ayrıca vekil kimliğini hendek ve bunun yerine bileşik bir PK (ve muhtemelen ters sütunlarda da bileşik bir benzersiz dizin) olur.

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

Bunu zorlaştıran sorguları söylemiyorsunuz, ancak her zaman bir Görünüm oluşturabilirsiniz.

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

Bunun oldukça eski olduğunu biliyorum, kazdığım için çok üzgünüm. Gereksiz ve fazladan fazladan yük getirmemek için ters dostluk endeksini tanımlamaması daha iyi olmaz mıydı ? Sahip olduğumuzdan ve bir PK olduğundan , tersine çevrilmiş olan da ne olursa olsun. UNIQUEINSERTPRIMARY KEY (a,b)UNIQUEKEY (b,a)UNIQUE
tfrommen

1
tf Quess optimizer’ine bağlı olan Guess. İşaret ettiğiniz gibi, sadece bir yönden kontrol etmek gereklidir, böylece ekleme planı yine de bunu yapabilir. Soru MySQL olarak etiketlendi - nasıl davrandığı hakkında hiçbir fikrim yok.
Martin Smith

Bunun eski bir cevap olduğunu biliyorum, ancak sadece bu konuda MySQL'in CHECK kısıtlamalarını tamamen görmezden geldiğini (ancak bunları başarılı bir şekilde ayrıştırmalarına rağmen) bu yüzden bu yaklaşımın o teknolojiyle ilerlemenin bir yolu olmadığını belirtmek istiyorum.
Micah,

@Micah doğru. 2012'de bunun farkında değildim. Yine de diğer DBMS'lerde çalışacak ...
Martin Smith

Bunun için bir Görünüm uygulamak için +1. AB ve BA'nın depolanması, (iki yönlü bir ilişki değilse) tutarsızlığa neden olur, oysa bu yöntem daha iyi bir yaklaşımdır
imans77

7

Bir "arkadaşlığın" her zaman iki yönlü / karşılıklı olduğunu varsayarsak, muhtemelen bunun gibi bir şeyle uğraşırdım.

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

Sonuç olarak, bunu "kişiden" kişiye "kişiden", "kişiden" ye "çoktan çoğa" birleşimine, "kişiliğe" kadar "arkadaşlık" olarak değiştirirsiniz. Bu, katılımları ve kısıtlamaları basitleştirecek, ancak ikiden fazla kişiye tek bir "dostlukta" izin vermenin yan etkisine sahip (belki de ek esneklik potansiyel bir avantaj olabilir).


Bu temelde bir grup / üyelik modelidir. Yine de ilginç bir fikir.
einSelbst

4

Sıra sayısını ikiye katlamak yerine arkadaşlıkların etrafındaki dizinleri tanımlamanız gerekebilir:

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

Bu şekilde, dizinler için depolama alanını ikiye katlarsınız, ancak tablo verileri için değil. Sonuç olarak, bu, disk alanında% 25'lik bir tasarruf olmalıdır. MySQL Query Optimizer yalnızca indeks aralığı taramalarını gerçekleştirmeyi seçecektir, bu nedenle indeks indeksleri kavramı burada iyi çalışır.

İşte Kapak Dizinleri ile ilgili bazı güzel linkler:

UYARI

Eğer arkadaşlık karşılıklı değilse, başka tür bir ilişki için temeliniz vardır: FOLLOWER

Friend_to bir friend_of arkadaşı değilse, bu ilişkiyi masanın dışında bırakabilirsiniz.

Tüm türler için ilişkileri tanımlamak istiyorsanız, karşılıklı olsun veya olmasın, muhtemelen aşağıdaki tablo düzenini kullanabilirsiniz:

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

İlişki tablosundan, aşağıdakileri içerecek şekilde ilişkileri düzenleyebilirsiniz:

  • Arkadaşlar karşılıklı olmalı
  • Düşmanlar karşılıklı olabilir veya olmayabilir
  • Takipçiler karşılıklı olabilir ya da olmayabilir
  • Diğer ilişkiler, değerlendirmeye tabi tutulacaktı (unutulmuş, terk edilmiş veya intikam alıcısı tarafından) (sabit)
  • Olası ilişkiler daha da genişletilebilir

İlişkinin karşılıklı olup olmadığına bakılmaksızın, bu tüm ilişkiler için daha sağlam olmalıdır.


merhaba @rolandomysqldba, ben cevaplarınızın büyük hayranıyım. bana gerçekten yardımcı oldu (bu durumda 1. örnek). Şimdi burada benim için bir uyarı, benzersiz bir ilişki istiyorum. (örn. A kullanıcısı B ile arkadaşsa, A ile B arkadaşı kabul edilmez.) Tetikle yapmalı mıyım? peki ya performans? çünkü çok büyük bir tablom var (yaklaşık 1 milyon kayıt) ve Kullanıcı A'nın arkadaşlarını (A, her ikisi de (friend_of, friend_to) alanlarında saklanır ve mysql'yi sadece bir indeks kullanarak ararsam, çok yavaş performans gösterir. Çift girişleri masamda saklamak zorundayım (örn. A-> B, B-> A) Herhangi bir daha iyi seçenek var mı?
Manish Sapkal

1

Uygulama içinde A'nın kimliğinin her zaman B'nin id'sinden düşük olduğunu kontrol edebilirseniz (A, B elementlerinin kimliklerini ön sipariş edin) VEYA olmadan sormadan yararlanabilirsiniz (sormak yerine id_A = a AND id_B = b'yi seçin) (id_A = a AND id_B = b) VEYA (id_A = b AND id_B = a)) ve ayrıca diğerinin yaklaşımlarıyla ihtiyaç duyacağınız kayıtların yarısını da saklayın. Öyleyse, ilişkinin durumunu korumak için başka bir alan kullanmalısınız (arkadaşlar, arkadaşlara, b'ye davet, arkadaşlara, arkadaşlara, b-arkadaşlara, b) ve bitirdiniz.

Bu benim arkadaşlık sistemimi yönetme şeklimdir ve sistemi basitleştirir ve diğer sistemlerde ihtiyaç duyacağınız satırın yarısını kullanır, sadece A kodundaki düşük id değerine eşittir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.