友情のためにリレーションシップテーブルを設計するにはどうすればよいですか?


33

A友人であればB、値ABとの両方を保存する必要BAがありますか、それとも1つで十分ですか?両方の方法の長所と短所は何ですか。

私の観察は次のとおりです。

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

現在、私は関係を一方向に維持しています。

ここに画像の説明を入力してください

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


プラットフォームにコミットしていますか、またはこれは理論的な質問ですか?
ニックチャマス

何ハイブリッドアプローチについて:モデル今日のSQL製品を使用して達成するために素敵な、それらのテーブルの正確に一つに挿入された友情を確保するため、別々のテーブルには、それぞれません報いと報われない友情:(
onedaywhen

@onedaywhen-ええ、グラフデータベースに適しています
ニックチャマス

@NickChammas:それは理論的な問題ではありません。mysqlAmazonクラウドに保存されているものに取り組んでいます。
チャン

1
@Chan-チェック制約を使用して関係を強制することはできないことを意味します(MySQLはこれらを強制しません)
Martin Smith

回答:


30

ABとBAを保存します。友情は実際には双方向の関係であり、各エンティティは別のエンティティにリンクされています。直感的には、「友情」は2人の人間の1つのリンクであると考えていますが、リレーショナルの観点からは、「Aには友人Bがいます」、「Bには友人Aがいます」に似ています。2つの関係、2つのレコード。


3
どうもありがとう。私は本当にあなたの考えを注意深く考える必要があります!ABとBAを保存しない理由は、ストレージがあるためです。なぜなら、友情があるたびに、テーブルが2倍保存されるからです。
チャン

1
記憶域については正しいですが、整数として格納する場合、友人と友人の関係はそれぞれ約30バイト(2レコードx 3列x整数あたり4バイト= 24バイトとパディング)を要することに注意してください。それぞれ10人の友人がいる100万人のデータは、まだ約300MBにすぎません。
datagod

1
datagod:そうです!
チャン

これが、ABとBAのテーブルの設計方法です。
kabuto178

2
さらに、ABのみが​​ありBAが存在しない状況では、これは「保留中のフレンドリクエスト」を表します。
グレッグ

13

友情が対称的であることを意図している場合(つまり、 A 友達となるBその逆はできません)、チェック関係を使用して一方向の関係を保存します。

また、サロゲート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

これはかなり古いので、掘り下げてごめんなさい。s に不要で冗長な余分な負担をかけないために、友情インデックスを 定義しない方が良いと思いませんか?我々が持っているので、とPKであるため、逆転もありませんどんな。UNIQUEINSERTPRIMARY KEY (a,b)UNIQUEKEY (b,a)UNIQUE
tfrommen

1
@tfquerŷオプティマイザーに依存すると思います。あなたが指摘しているように、挿入計画がとにかくこれを行うかもしれないので、一方向だけをチェックする必要があります。質問にはMySQLというタグが付けられています。それがどのように動作するのかわかりません。
マーティンスミス

私はこれが古い答えであることを知っていますが、これにつまずいた人にMySQLがCHECK制約を完全に無視することを指摘したいだけです(ただし、それらを正常に「解析」します)ので、このアプローチはおそらくそのテクノロジーを使用する方法ではありません。
ミカ

@Micah true。私はまだ他のDBMSで動作します... 2012年にそれを認識していなかった
マーティン・スミス

そのためのビューを実装するための+1。この方法は、より良いアプローチであるのに対し、(関係は双方向でない場合)、ABおよびBAが格納有する矛盾にもたらす
imans77が

7

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

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人の「友情」で3人以上を許可するという副作用があります(ただし、追加の柔軟性が潜在的な利点になります)。


これは基本的にグループ/メンバーシップパターンです。しかし、興味深いアイデア。
einSelbst 14

4

行数を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倍にします。その結果、これによりディスクスペースが25%節約されるはずです。MySQL Query Optimizerは、インデックス範囲のスキャンのみを実行するように選択するため、ここではインデックスをカバーするという概念がうまく機能します。

カバリングインデックスに関する便利なリンクを次に示します。

警告

友情が相互関係でない場合、別のタイプの関係の基礎があります。フォロワー

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

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

  • 友人は相互に
  • 敵は相互かそうでないか
  • フォロワーは相互かそうでないか
  • その他の関係は、解釈の対象となります(忘れられた、見捨てられた、または復ofの受信者(修正済み))
  • 可能性のある関係をさらに拡張できます

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


こんにちは@rolandomysqldba、私はあなたの答えの大ファンです。私にとっては本当に役立ちます(この場合は最初の例)。今ここに私のための1つの警告があります、私はユニークな関係が欲しいです。(たとえば、ユーザーAがBと友達の場合、AとBの友達は受け入れられません。)トリガーを使用する必要がありますか?そしてパフォーマンスはどうですか?非常に大きなテーブル(約100万件のレコード)があり、ユーザーAの友人(Aが両方の(friend_of、friend_to)フィールドに格納され、mysqlが1つのインデックスのみを使用して検索する場合、パフォーマンスが非常に遅いため)私は(eg.A-> B、B-> A)私のテーブルに重複したエントリを格納するために必要な任意のより良いオプションを選択します。?
マニッシュSapkal

1

アプリケーション内でAのidが常にBのidより小さいことを制御できる場合(A、B要素のidを事前に注文)、ORなしで尋ねることを活用できます(尋ねる代わりに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)、これで完了です。

これが私のフレンドシップシステムの管理方法です。これにより、システムが簡素化され、他のシステムで必要な行の半分が使用されます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.