web-dev-qa-db-ja.com

友情の関係テーブルをどのように設計すればよいですか?

ABのフレンドである場合、ABBAの両方の値を保存する必要がありますか、それとも十分ですか?両方の方法の長所と短所は何ですか。

これが私の観察です:

  • 両方を保持している場合、友達からリクエストを受け取ったときに両方を更新する必要があります。
  • 両方を保持しないと、このテーブルで複数のJOINを実行しなければならないときに困難を感じました。

現在、私はこの関係を一方通行にしています。

enter image description here

この場合、どうすればよいですか?何かアドバイス?

34
Chan

ABとBAを保管します。友情は実際には双方向の関係であり、各エンティティは別のエンティティにリンクされています。直観的には「友情」を2人のつながりの1つと捉えていますが、関係的には「Aには友達Bがいる」「Bには友達Aがある」という意味です。 2つの関係、2つのレコード。

30
datagod

友情が対称であることを意図している場合(つまり、ABと友達になることはできませんが、その逆はできません)、チェック制約を使用して一方向の関係を保存します。各関係は一方向にしか表現できないことに注意してください。

また、サロゲートIDを破棄し、代わりに複合PKを使用します(逆の列にも複合一意インデックスがある可能性があります)。

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

これが難しくなるクエリを言うのではなく、いつでもビューを作成できます

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

「友情」は常に双方向/相互であると想定すると、おそらくこのように扱うでしょう。

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

結果は、多対多の結合を「人」から「人」に、多対多の結合を「人」から「友情」に変更することです。これにより、結合と制約が簡素化されますが、1人の "友情"に2人以上のユーザーを許可するという副作用があります(ただし、柔軟性の追加が潜在的な利点になる可能性があります)。

7
db2

行数を2倍にするのではなく、友情に関するインデックスを定義する必要がある場合があります。

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

この方法では、インデックスのストレージを2倍にしますが、テーブルデータのストレージは2倍にしません。結果として、これはディスクスペースの25%の節約になるはずです。 MySQL Query Optimizerは、インデックス範囲スキャンのみを実行することを選択します。これが、インデックスをカバーするという概念がここでうまく機能する理由です。

カバリングインデックスに関する素晴らしいリンクをいくつか示します。

警告

友情が相互ではない場合、あなたは別のタイプの関係の基礎を持っています:FOLLOWER

Friend_toがfriend_ofのフレンドではない場合は、その関係をテーブルから除外できます。

相互に関係があるかどうかに関係なく、すべてのタイプの関係を定義する場合は、おそらく次のテーブルレイアウトを使用できます。

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

関係テーブルから、以下を含むように関係を調整できます。

  • 友達は相互である必要があります
  • 敵は相互かどうか
  • フォロワーは相互であるかどうか
  • その他の関係は、解釈の対象となります(忘れられたか、見捨てられたか、または復讐の受信者(固定)によって)
  • 可能性のある関係をさらに拡張することができます

これは、関係が相互であるかどうかに関係なく、すべての関係に対してより堅牢になるはずです。

4
RolandoMySQLDBA

アプリケーション内でAのIDが常にBのIDよりも低いことを制御できる場合(A、B要素のIDを事前に注文する)、OR(select where id_A = a AND id_B = b、(id_A = a AND id_B = b)OR(id_A = b AND id_B = a))の代わりに、必要なレコードの半分を維持する次に、別のフィールドを使用して関係の状態を維持する必要があります(are-friends、a-solicited-to-b、b-solicited-to-a、exfriends-a、exfriends-b)、そしてあなた完了です。

これは、フレンドシップシステムを管理する方法であり、これによりシステムが簡略化され、他のシステムで必要となる行の半分が使用されます。Aはコード内のより低いID値に等しいというだけです。

1
appartisan