LEFT JOIN 사용 시 주의해야 할 점

최인준·2024년 1월 21일
6
post-thumbnail

서론

본 포스팅의 예제 쿼리는 MySQL 기준입니다.

SQL을 사용하면서 join은 필수적이라고 볼 수 있다.

원하는 결과를 얻기 위해선 여러 테이블의 조합이 필요하기 때문에 join이 항상 필요하다.

그 중에서도 많이 쓰이는 join 종류를 꼽자면 inner join과 left join 인 것 같다.

이번 포스팅에서는 left join에 대해 다뤄볼 예정이다. 간단하게 left join에 대해서와 언제 쓰일 수 있는 지, 그리고 주의해야할 점을 언급하며 실습을 통해 주의점을 소개할 것이다.

그동안 나는 left join을 쓸 일을 많이 겪어본 것은 아니지만 쓸 때는 테이블의 순서를 딱히 고려하지 않았었다.

근데 left join 시 테이블의 순서에 따라 결과, 성능이 달라질 수 있다는 글들을 접했고 이에 대해 직접 실습해보았다.

먼저 left join에 대해 간략히 살펴보자.

LEFT JOIN

LEFT JOIN이란 쉽게 생각하면 왼쪽 테이블이 주인공이 되는 조인이다.

다음 그림을 보며 쉽게 이해해보자!

예시 이미지에는 테이블이 두개가 있다. left join을 하게 되면 이미지의 left table이 주인공이 된다.

이미지를 보면 왼쪽 테이블은 모든 레코드를 반환하지만 오른쪽 테이블은 왼쪽 테이블과 일치되는 값만 반환된다.

즉, 왼쪽 테이블이 주인공이 되어 주인공에게 모두 맞춰주는 것이다. 왼쪽 테이블의 CountryId의 4라는 값이 실제로 오른쪽 테이블에는 없지만 왼쪽 테이블이 주인공이기 때문에 결과로는 반환이 되고 오른쪽 테이블의 값이 null로 반환된다.

LEFT JOIN의 활용

그렇다면 LEFT JOIN을 언제 활용할 수 있을까?

예를 들어 Player라는 테이블과 Team이라는 테이블이 있다고 해보자.

Player가 속한 Team을 조회하고 싶을 때 inner join을 통해 결과를 반환 받을 수 있다.

하지만 속해있는 Team이 없는 Player까지 포함하여 모두 조회하고 싶을 때는 Player 테이블이 주인공이 되는 LEFT JOIN을 활용하여 결과를 반환 받을 수 있다. 그러면 속해있는 Team이 없는 Player의 Team 필드는 null로 표시되어 반환받을 수 있다.

first_namelast_namephone_numberteam_nmae
injunchoi010-3434-2323Liverpool
youngheekim010-5454-3434Liverpool
chulsulee010-1212-1212Tottenham
minsupark010-6565-6565Tottenham
sonlim010-9898-9898null

LEFT JOIN시 주의점

LEFT JOIN은 많이 쓰이는 만큼 주의해야 할 점을 아는 것도 중요하다.

실제 쿼리를 날려보며 볼 것이기 때문에 예제 테이블을 준비해두었다.

예제 테이블

team

idteam_nmae
1Liverpool
2Tottenham

player

player_idposition_idteam_idfirst_namelast_namephone_number
111injunchoi010-3434-2323
221youngheekim010-5454-3434
3null2chulsulee010-1212-1212
422minsupark010-6565-6565
5nullnullsonlim010-9898-9898

position

idposition_nameprefer_rate
1ST3
2CB5
3CM1
4LW2
5RB4

실습

주의해야 할 점은 크게 두가지를 소개할 것이다.

첫번째 주의점

첫번째로는 SELECT문에 가장 많은 열을 가져와야 하는 테이블을 우선적으로 명시해야한다

예시 테이블을 보고 쿼리를 날려보며 보도록 하겠다.

가정한 상황은 다음과 같다.

  • 선수의 성, 이름, 전화번호, 속한 팀, 배정된 포지션을 조회해야 한다.
  • 팀에 속해있지 않더라도, 배정된 포지션이 없더라도 선수는 모두 조회되어야 한다.

첫번째 쿼리는 주의점을 지키며 작성해보자. selelct문에 player의 컬럼이 제일 많이 들어가므로 player테이블을 먼저 명시해준다.

select p.first_name, p.last_name, p.phone_number, t.team_name, po.position_name
from player as p
left join team as t on t.id = p.team_id
left join position as po on p.position_id = po.id;

이에 대한 결과는 다음과 같다.

first_namelast_namephone_numberteam_nameposition_name
injunchoi010-3434-2323LiverpoolST
youngheekim010-5454-3434LiverpoolCB
chulsulee010-1212-1212Tottenhamnull
minsupark010-6565-6565TottenhamCB
sonlim010-9898-9898nullnull

원하는 상황대로 선수가 모두 조회되었고 팀이 없거나 포지션이 없는 선수는 해당 컬럼이 null로 반환되었다.

그럼 주의점을 어기고 team 테이블을 먼저 적어주면 어떻게 될까.. 실행해보자.

select p.first_name, p.last_name, p.phone_number, t.team_name, po.position_name
from team as t
left join player as p on t.id = p.team_id
left join position as po on p.position_id = po.id;

from 절을 보면 player가 아닌 team이 들어가있다. 결과는 다음과 같다.

first_namelast_namephone_numberteam_nameposition_name
youngheekim010-5454-3434LiverpoolCB
injunchoi010-3434-2323LiverpoolST
minsupark010-6565-6565TottenhamCB
chulsulee010-1212-1212Tottenhamnull

뭔가 다르다. 위의 테이블은 5개의 레코드를 반환받았는데 이번엔 4개의 레코드만 반환되었다.

LEFT JOIN의 특성대로 team 테이블이 주인공이 되어서 team에 초점이 맞춰진 것이다.

그렇기에 주인공이 아닌 Player 테이블의 속해있는 team이 없는 레코드는 반환되어지지 않았다🥲

가정된 상황 의도대로 이루어지지 않은 것이다.

이렇게 어떤 테이블이 먼저 오느냐에 따라 LEFT JOIN에서는 다른 결과를 받는다.

INNER JOIN이라면 어떨까? 위 두 쿼리가 둘다 INNER JOIN이라면 둘의 쿼리는 결과가 동일하다.

INNER JOIN은 두 테이블의 일치하는 값에 대해서만 결과가 반환되기 때문이다.

LEFT JOIN을 사용하게 된다면 위에서 본 것 처럼 INNER JOIN을 사용할 때와 마냥 비슷하게 생각하지는 말자.

테이블의 순서에 따라 결과가 달라질 수 있으니 고려사항이 추가된 것이다.

두번째 주의점

두번째로 주의해야 할 점은 LEFT JOIN 이후에 INNER JOIN이 나오면 안된다는 것이다

사실 이는 어찌보면 당연한 것이다.

LEFT JOIN을 하는 이유를 생각해보자. 여러 테이블이 있을 때 테이블 간 일치하는 값이 있든 없든 주인공 테이블 기준으로 모두 가져오려함이 목적이다.

근데 중간에 테이블간 일치하는 값만 반환하는 INNER JOIN을 사용한다는 것 자체가 LEFT JOIN을 활용하는 의미가 없어진다.

예시를 보자. 위의 예제쿼리에서 첫번째 쿼리는 가정한 상황 의도대로 결과가 잘 반환되었다. 그 쿼리에서 Position을 join 하는 부분을 기존 LEFT JOIN 방식에서 INNER JOIN으로 바꾸고 쿼리를 날려보겠다.

select p.first_name, p.last_name, p.phone_number, t.team_name, po.position_name
from player as p
left join team as t on t.id = p.team_id
**inner join** position as po on p.position_id = po.id

이 쿼리에 대한 결과를 join 별로 나누어 먼저 생각해보자.

먼저 player와 team을 LEFT JOIN한다. player의 원하는 컬럼이 조회되고 속해있는 팀이 있다면 팀 이름까지 반환되고 속해있는 팀이 없다면 해당 값이 null로 반환될 것이다. 여기까지의 결과는 다음과 같다.

first_namelast_namephone_numberteam_name
injunchoi010-3434-2323Liverpool
youngheekim010-5454-3434Liverpool
chulsulee010-1212-1212Tottenham
minsupark010-6565-6565Tottenham
sonlim010-9898-9898null

그 다음 position에 대해 INNER JOIN이 실행된다. 그럼 배정받은 포지션이 없는 선수는 INNER JOIN 조건에

일치하지 않아 결과에서 제외된다. 여기서는 3번째 선수와 5번째 선수는 포지션이 없어 결과에 반환되지 않을 것이다.

그렇게 돼서 최종 결과는 다음과 같다.

first_namelast_namephone_numberteam_nameposition_name
injunchoi010-3434-2323LiverpoolST
youngheekim010-5454-3434LiverpoolCB
minsupark010-6565-6565TottenhamCB

위에서 단계별로 결과를 예측한대로 나오게 되었다.

이 결과를 반환받았다는 것은 이전에 수행한 LEFT JOIN 작업이 의미가 있는걸까? 라는 생각이 든다.

LEFT JOIN을 통해 수행된 작업이 INNER JOIN으로 인해 아무 의미가 없어졌다..😂

주의해야 할 점 두가지를 실습을 통해 봤으니 이해가 더 수월하다는 생각이 든다!

결론

이번 포스팅에서의 핵심은 LEFT JOIN 사용 시 주의해야할 점이고 크게 두가지를 소개했다.

  • SELECT문에 가장 많은 열을 가져와야 하는 테이블을 우선적으로 명시해야한다.
  • LEFT JOIN 이후에 INNER JOIN을 사용하면 안된다.

이 두가지를 실습을 통해 보았다.

LEFT JOIN은 INNER JOIN만큼이나 많이 쓰이기 때문에 주의해야할 점은 반드시 알아놓아야 하는 것 같다.

이번에는 쿼리에 따라 결과가 달라질 수도 있는 실습을 해보았는데 LEFT JOIN은 쿼리에 따라 성능이 달라지는 경우도 있다. 포스팅이 길어져 성능에 관한 얘기는 다음 포스팅으로 미뤄야겠다!

0개의 댓글