😱 삽질 이슈 기록

LEFT (OUTER) JOIN 할때 결과 중복 이슈

cocoroocoo 2022. 11. 11. 22:51

문제 상황

매출 대시보드 작업 중 매출액이 2배 이상 으로 높게 잡히는 문제가 발생했다!

 

처음에는 매출데이터를 여러번 집어넣게 된 것은 아닌지 의심을 했다.

 

팀원 분들 도 여러 의견을 주셨는데,

매출데이터를 넣기전에 해당 날짜의 매출데이터가 이미 저장되어있는지 한번 확인을 하는데

호출이 여러번 되면서 동시성 문제가 생기게 되어 중복 체크가 제대로 되지 않은 것은 아닌지 등 여러 의견을 주셨다.

 

하루에 한번 업로드 하기 때문에

매출 데이터 업로드 로그를 확인해보았는데, 절대로 여러번 업로드가 일어나지 않았고,

실제 업로드 된 데이터도 발생 데이터 갯수 만큼 잘 업로드가 되어있었다.

 

 

원본 데이터는 제대로인데 어째서 계산 결과가 엄청 뛰게 된걸까?

 

 

원인 분석

매출데이터를 한번 조인 해서 뷰테이블을 만든 후 그 뷰테이블로 데이터를 그리는데 거기서 문제가 생긴 것이였다. 

 

LEFT (OUTER) JOIN 할때 왼쪽 테이블에만 집중하는 경우,
예상치 못한 결과 가 생길 수 있다. 😱

 

LEFT OUTER JOIN(= LEFT JOIN)은
왼쪽 테이블 기준으로 오른쪽 테이블상에 연결된 정보가 있으면 컬럼을 가져오고 없으면 NULL로 채워준다.


그런데 오른쪽 테이블에 연결 정보가 중복으로 있을 경우
결과 테이블의 카디널리티가 늘어날 수 있다.

왼쪽 테이블 정보 기준으로 오른쪽 테이블에 연결정보가 있으면 연결정보 토대로 새로운 정보를 채우고, 아니면 NULL로 채운다.

 

1. 일반적인 경우

 

SELECT * FROM A
LEFT JOIN B
ON A.id = B.id;

 

 

2. 오른쪽 테이블에 연결 정보 중복이 있을 경우

SELECT * FROM A
LEFT JOIN B
ON A.id = B.id;

 

중복 된 만큼 카디널리티가 늘어났다.

 

중복이 없도록 테이블 설계를 잘 하는 것이 우선이겠지만,

중복이 이미 생겨버렸고, 제거 하기도 힘든 상황이라면

 

GROUP BY 를 통해 연결하려는 오른쪽 정보의 연결정보당 row 하나씩만 남기도록 해야한다.

 

해결

GROUP BY 후 JOIN 수행

SELECT * FROM A
LEFT JOIN ( 
  SELECT 
  	id, 
  	MAX(grade)
  FROM B
  GROUP BY B.id
) AS B
ON A.id = B.id;

 

 

 

TIP. 쿼리 플레이그라운드 사이트

https://www.db-fiddle.com/