Self Join(셀프 조인)은 언제 써야할까?

태그
데이터베이스
SQL

노트

  • 셀프 조인은 언제 써야할까? 대표적인 3가지 상황 예시를 다음과 같이 들어본다
    • 특정 Column 이 자신 테이블의 PK 인 경우 (1) - 게시글 댓글의 답글 (계층 구조)
      • comments(댓글) 라는 테이블이 있다. 댓글에 대해 답글을 다는 기능을 넣는다고 생각해보자.
      • id(PK)
        parent_comment_id(FK)
        comment
        1
        NULL
        hello
        2
        1
        hi
      • 자신의 부모 댓글을 지정하는 칼럼인 parent_comment_id에 해당하는 comment_id 가 들어가게 된다.
      • 이 때 댓글을 불러올때 각 댓글의 답글 리스트를 붙여서 불러오고 싶다고 생각해보자.
      • 다음과 같이 Self Join을 수행해야 할 것이다.
      • SELECT * FROM comments c JOIN comments sub ON c.id = sub.parend_comment_id WHERE c.parent_comment_id is not null
         
    • 특정 Column 이 자신 테이블의 PK 인 경우 (2) - 연인 관계 매핑 (자신의 참조 결과 포함)
      • couple(커플) 이라는 테이블이 있다. 자신의 연인 정보(lover_id)도 함께 들어가는 테이블이다.
      • lover_id 는 자신의 연인의 ID 값이며, 동일한 테이블에 다른 row로 존재한다.
        • id(PK)
          name
          lover_id(FK)
          1
          a
          2
          2
          b
          1
          3
          c
          4
          4
          d
          3
          5
          e
          6
      • 이 때 각 커플들의 두 사람 모두의 정보를 가져와야 하는 경우(남자의 이름, 여자의 이름)를 생각해보자.
      • 이때 lover(상대방)의 name도 가져오고 싶다? 그러기 위해 Self join 을 사용할 수 있다.
      • SELECT me.name, lv.name from couple me JOIN couple lv ON me.id = lv.lover_id
       
    • PK가 UUID 같은 랜덤 값 인데, 커서 기반 페이지네이션을 구현하려는 경우 - 뉴스 피드 페이지네이션 (커버링 인덱스를 만족하는 쿼리 구현)
      • 예를 들어, 뉴스 피드(feed) 라는 테이블이 있다. PK는 UUID 라고 가정해보자.
        • id(PK)
          created_at
          title
          ?
          2023-09-09
          a
          ?
          2023-09-07
          b
      • 여기서, 최신순으로(createdAt의 내림차순) 피드를 가져오는 쿼리를 작성할 것이고 테이블은 다음과 같다.
      • 그런데 페이지네이션을 고려해서 어떤 특정 뉴스 ID가 주어졌을 때 해당 뉴스 ID 이전에 생성된 뉴스들을 가져오는 쿼리를 작성한다고 생각해보자. (다만, 성능을 고려해, 커서 기반으로 할 것이다, created_at은 인덱스가 걸려있다 가정하면)
      • 그런데 ID가 Auto Increment 또는 특정 순서로 정렬되어 있지 않다면? 그냥 SELECT 쿼리와 WHERE Range 쿼리를 쓸 수 없다. 따라서
        • 서브쿼리를 작성하거나
        • 조인을 수행해야한다.
      • 이 때 조인을 만약 수행한다면, 당연히 자기 자신 테이블을 수행해야 한다.
      • 물론, 그냥 LIMIT OFFSET 과 WHERE 을 쓰거나 Auto Increment 를 쓰면 되는거 아니냐? 일수도 있지만, 그렇게 할 수 없는 경우도 있다.
      • created_at을 cursor 키로 쓰면 되는거 아니냐? 라는 의문이 들 수 있지만, created_at은 UNIQUE 값이 아닌 특성이 있다는 걸 고려해보자.
      • 참고로 이는 어떻게든 커버링 인덱스 조건을 만족하도록 하려는 성능적 특성을 고려하는 쿼리라고 볼 수 있다.

요약

📌
요약: 테이블내 데이터가 계층적이거나, 자신을 참조해서 결과를 보여줘야 하거나, 커버링 인덱스를 만족시키기 위한 쿼리를 만드는 일부 경우.