우정을위한 관계 표를 어떻게 설계해야합니까?


33

A의 친구 인 경우 BAB과를 모두 저장해야합니까 BA, 아니면 하나만으로 충분합니까? 두 가지 방법의 장단점은 무엇입니까?

내 관찰은 다음과 같습니다.

  • 둘 다 지키면 친구의 요청을받을 때 두 가지를 모두 업데이트해야합니다.
  • 둘 다 지키지 않으면 JOIN이 테이블로 여러 번해야 할 때 어려움을 겪었습니다 .

현재는 관계를 일방적으로 유지합니다.

여기에 이미지 설명을 입력하십시오

이 경우 어떻게해야합니까? 어떤 충고?


플랫폼에 전념하고 있습니까, 아니면 이론적 인 질문입니까?
Nick Chammas

하이브리드 접근 방식에 대해 무엇 : 모델 requited 각각 별도의 테이블에 보답 우정, 우정을 확인하는 것은 해당 테이블의 정확히 하나에 삽입되어, 오늘날의 SQL 제품을 :( 사용하여 달성하기 좋은하지
onedaywhen

@onedaywhen-그래, 그래프 데이터베이스에 더 적합하게 들린다 .
Nick Chammas

@NickChammas : 이론적 인 질문이 아닙니다. mysqlAmazon 클라우드에 저장된 작업 중입니다.
Chan

1
@Chan-아, 그것은 당신이 관계를 시행하기 위해 점검 제약 조건을 사용할 수 없다는 것을 의미합니다 (MySQL은 이것을 강제하지 않습니다)
Martin Smith

답변:


30

나는 AB와 BA를 저장할 것이다. 우정은 실제로 양방향 관계이며 각 엔터티는 서로 연결되어 있습니다. 직관적으로 우리는 "우정"을 두 사람 사이의 하나의 링크로 생각하지만 관계적인 관점에서 보면 "A는 친구 B가있다"와 "B는 친구 A가있다"와 더 비슷합니다. 두 가지 관계, 두 가지 기록.


3
많은 감사합니다. 나는 당신의 생각에 대해 신중하게 생각해야합니다! AB와 BA를 저장하지 않는 이유는 스토리지 때문입니다. 우정을 가질 때마다 내 테이블이 두 배나 많이 저장됩니다.
Chan

1
스토리지에 대해서는 맞지만 정수로 저장된 경우 각 친구-친구 관계는 약 30 바이트 (2 개의 레코드 x 3 개의 열 x 정 수당 4 바이트 = 24 바이트에 약간의 패딩)가 소요됩니다. 각각 10 명의 친구를 가진 백만명의 사람들은 여전히 ​​약 300MB의 데이터 일 것입니다.
datagod

1
datagod : 맞습니다!
Chan

이것이 AB & BA 테이블 디자인 방식입니다.
kabuto178

2
또한 BA가 아닌 AB 만있는 상황에서는 '보류중인 친구 요청'을 나타낼 수 있습니다.
그렉

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

나는 이것이 매우 오래되었다는 것을 알고 있으므로 이것을 파고해서 죄송합니다. 불필요하고 중복되는 추가 부담을 피하기 위해 리버스 우정 지수를 정의하지 않는 것이 낫지 않습니까? 우리가 가지고 있고 PK가이므로 , 그 반대 도 마찬가지 입니다. UNIQUEINSERTPRIMARY KEY (a,b)UNIQUEKEY (b,a)UNIQUE
tfrommen

1
@tf 추측은 querŷ 옵티 마이저에 의존합니다. 지적했듯이 삽입 계획이 어쨌든이를 수행 할 수 있도록 한 방향으로 만 확인하면됩니다. 이 질문에는 MySQL 태그가 지정되어 있습니다.
Martin Smith

나는 이것이 오래된 대답이라는 것을 알고 있지만, MySQL이 CHECK 제약 조건을 완전히 무시한다는 것을 지적하고 싶습니다 (성공적으로 구문 분석 할 것입니다).
Micah

@Micah 사실. 2012 년에는 그 사실을 몰랐습니다. 여전히 다른 DBMS에서 일할 것입니다.
Martin Smith

이를 위해보기를 구현 한 +1 AB & BA를 저장하면 불일치가 발생하지만 (관계가 양방향이 아닌 경우)이 방법이 더 나은 방법입니다.
imans77

6

"우정"이 항상 양방향 / 상호라고 가정하면 아마 이런 식으로 처리 할 것입니다.

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

그 결과 "person"에서 "person"으로 다 대다 조인에서 "person"에서 "friendship"으로 다 대다 조인으로 변경됩니다. 이렇게하면 조인과 제약이 간단 해지지 만 단일 "우정"으로 두 사람 이상이 허용되는 부작용이 있습니다 (추가 유연성이 잠재적 이점 일 수 있음).


이것은 기본적으로 그룹 / 멤버쉽 패턴입니다. 그래도 재미있는 아이디어입니다.
einSelbst

4

행 수를 두 배로 늘리지 않고 우정에 대한 색인을 정의해야 할 수도 있습니다.

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

이 방법으로 인덱스 스토리지를 두 배로 늘리지 만 테이블 데이터는 스토리지를 두 배로 늘리지 않습니다. 결과적으로 디스크 공간이 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');

관계 테이블에서 다음을 포함하도록 관계를 정렬 할 수 있습니다.

  • 친구는 서로 있어야합니다
  • 적들은 서로의 관계 일 수도 있고 아닐 수도 있습니다
  • 추종자는 서로 또는 아닌
  • 다른 관계는 (잊혀지거나 잊혀진 사람 또는 복수의 수령인 (고정 된) 사람에 의해) 해석 될 수 있습니다.
  • 가능성 관계를 더 확장 할 수 있습니다

관계가 상호 관계에 관계없이 모든 관계에 대해보다 강력해야합니다.


안녕하세요 @rolandomysqldba, 나는 당신의 답변의 큰 팬입니다. 정말 도움이되었습니다 (이 경우 첫 번째 예). 이제 여기에 하나의 경고가 있습니다. 나는 독특한 관계를 원합니다. (예 : B의 ​​사용자 A 친구 인 경우 A의 B 친구는 허용되지 않습니다.) 트리거로해야합니까? 성능은 어떻습니까? 매우 큰 테이블 (약 백만 개의 레코드)이 있고 사용자 A의 친구를 검색하면 (A는 (friend_of, friend_to) 필드에 저장되고 mysql은 하나의 색인 만 사용하여 매우 느리게 수행됩니다. 테이블에 중복 항목을 저장해야합니다 (예 : A-> B, B-> A)
Manish 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)) 또한 상대방의 근사값으로 필요한 레코드의 절반을 유지합니다. 그런 다음 다른 필드를 사용하여 관계의 상태를 유지해야합니다 (친구, 요청자, b 자, 요청자, 친구, 친구).

이것이 내가 우정 시스템을 관리하는 방식이며 시스템을 단순화하고 다른 시스템에 필요한 행의 절반을 사용하며 A는 코드의 id 값이 낮습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.