class="layout-aside-right paging-number">
본문 바로가기
TIL

[TIL168] solvesql - 세 명의 사용자가 서로 친구인 관계 찾기 (Triangle 관계 탐색)

by heestory323 2026. 6. 23.

문제 설명

Facebook 사용자 간 친구 관계가 edges 테이블에 저장되어 있다.

 

문제에서는

ID가 3820인 사용자를 포함한 세 명의 사용자가 서로 친구인 경우를 찾아야 한다.

여기서 중요한 점은

단순히

3820 ↔ 100
3820 ↔ 200

만 존재하는 것이 아니라,

3820 ↔ 100
3820 ↔ 200
100  ↔ 200

처럼 세 사람 모두가 서로 친구여야 한다는 것이다.

 

또한 중복 제거를 위해

user_a_id < user_b_id < user_c_id

를 만족하는 경우만 출력해야 한다.

 

출력 컬럼

user_a_id
user_b_id
user_c_id

내가 처음 생각한 접근

처음에는

세 명이 친구 관계

↓

셀프 조인인가?

라는 생각이 들었다.

하지만 막상 빈 쿼리창을 보니

어디를 조인해야 하지?

조인 조건은 뭘 써야 하지?

A, B, C는 어떻게 정하지?

가 전혀 떠오르지 않았다.

그래서 처음에는

UNION ALL
+
users 테이블

등을 섞어서 접근하려고 했다.

 

하지만 문제를 다시 읽어보니

핵심은 사용자가 아니라

친구 관계 자체

를 연결하는 것이었다.


내가 헷갈렸던 부분

1. "세 명이 친구"를 SQL로 어떻게 표현하지?

처음에는

세 명이 친구

라는 말을 보고

막연하게

친구가 3명 이상?

정도로 이해했다.

하지만 실제 문제는

A와 B 친구

A와 C 친구

B와 C 친구

를 모두 만족해야 했다.

 

A-B

B-C

A-C

관계가 모두 존재해야 한다.


2. JOIN 조건을 어떻게 만들어야 하지?

가장 많이 막혔던 부분.

처음에는

JOIN은 알겠는데

ON 절을 어떻게 써야 하지?

였다.


이 문제를 이해한 결정적인 방법은

A, B, C를 먼저 종이에 적는 것이었다.

A-B

B-C

A-C


그리고 각각 의 위치를

[테이블 ] a , b 라고 할 때, 다음과 같이 관계도가 형성된다.

[ t1 ] A-B

[ t2 ] B-C

[ t3 ] A-C

 


그러면 자연스럽게

t1의 B

=

t2의 A

가 되어야 연결된다.

 

그래서

t1.b = t2.a

가 나온다.


A-C 관계

를 확인하기 위해

t1의 A

=

t3의 A
t2의 C

=

t3의 C

가 되어야 한다.

그래서

t1.a = t3.a

t2.b = t3.b

가 나온다.


결국 이 문제의 핵심 조건은

t1.b = t2.a

t2.b = t3.b

t1.a = t3.a

였다.


3. JOIN 조건은 어느 ON 절에 써야 할까?

이 부분도 헷갈렸다.

예를 들어

JOIN t2
ON t1.b = t2.a
AND t2.b = t3.b

처럼 쓰고 싶었는데

 

생각해보면

t3는 아직 등장하지 않았다.

t1
t2

까지만 존재하는 상태에서

t3

를 참조할 수 없다.


그래서

JOIN t3
ON t1.a = t3.a
AND t2.b = t3.b

처럼

해당 테이블이 등장한 이후 ON 절에서 사용해야 한다.


4. UNION과 UNION ALL은 왜 다를까?

친구 수를 세는 문제에서는

UNION ALL

을 사용했었다.

왜냐하면

1 → 2

2 → 1

두 관계가 모두 필요했기 때문이다.


하지만 이번 문제는

친구 관계 존재 여부

가 중요했다.

 

중복된 관계가 많아지면

같은 삼각형을 여러 번 찾을 수 있다.

그래서

UNION

으로 중복을 제거하였다.


최종 해결 방법

1단계. 친구 관계를 양방향으로 변환

원본 데이터는

1 ↔ 2

형태이다.

이를

1 → 2

2 → 1

형태로 변환한다.

이를 위해

UNION

사용.


2단계. A-B 관계 찾기

3단계. B-C 관계 찾기

t1.b = t2.a

4단계. A-C 관계 존재 여부 확인

t1.a = t3.a

t2.b = t3.b

5단계. 3820 포함 여부 확인

문제 조건에 따라

t1.a = 3820
OR t1.b = 3820
OR t2.b = 3820

를 적용한다.


6단계. 중복 제거

t1.a < t1.b
AND t1.b < t2.b

조건을 통해

A < B < C

를 만족하는 경우만 남긴다.


정답 쿼리

WITH t AS (
    SELECT
        user_a_id AS a,
        user_b_id AS b
    FROM edges

    UNION

    SELECT
        user_b_id AS a,
        user_a_id AS b
    FROM edges
)

SELECT
    t1.a AS user_a_id,
    t1.b AS user_b_id,
    t2.b AS user_c_id

FROM t t1

JOIN t t2
    ON t1.b = t2.a

JOIN t t3
    ON t1.a = t3.a
   AND t2.b = t3.b

WHERE
(
    t1.a = 3820
    OR t1.b = 3820
    OR t2.b = 3820
)

AND t1.a < t1.b
AND t1.b < t2.b;

핵심 SQL 개념 정리

Self Join

이번 문제에서는 같은 테이블을 여러 번 사용했다.

중요한 것은

t1

t2

t3

첫 번째 테이블
두 번째 테이블
세 번째 테이블

로 보는 것이 아니라

A-B 관계

B-C 관계

A-C 관계

로 이해하는 것이다.


UNION

이번 문제에서는 친구 관계를 양방향으로 만들기 위해 사용했다.

1 → 2

2 → 1

를 모두 생성한다.

또한 중복 제거를 통해 동일한 관계가 여러 번 생성되는 것을 방지한다.


JOIN 조건 설계

JOIN 조건은 외우는 것이 아니라

먼저 필요한 관계를 그림으로 표현해야 한다.

A-B

B-C

A-C

같은 사람끼리 연결

t1.b = t2.a

t2.b = t3.b

t1.a = t3.a

이번 문제에서 배운 패턴

내 생각

세 명이 친구라는 게 뭘 의미하지?

A-B / B-C / A-C 관계로 분해

각 관계를 t1, t2, t3로 지정

같은 사람끼리 JOIN

3820 포함 여부 필터링

A < B < C 조건으로 중복 제거

정답


한 줄 요약

"세 명이 서로 친구" 문제는 A-B, B-C, A-C 세 관계를 먼저 정의한 뒤, 같은 사람을 기준으로 Self Join하여 연결하는 그래프(네트워크) 문제였다.

 

 


 

문제풀이 완료 !