서브쿼리를 조인으로 대체하기

노력을 즐겼던 사람·2020년 8월 14일
1

사오정 앱개발

목록 보기
8/11
post-thumbnail

서버를 개발하다가 외부 테이블에 특정한 조건에 해당하는 로우가 존재하는지 체크해야할 필요가 있었다. 그 로직을 처음에는 서브쿼리를 통해서 해결을 했는데 알고보니 서브쿼리는 왠만하면 사용을 하지 말라고 하더라(나중에 왜 사용하면 안되는지 정확히 알아봐야겠다) 그래서 어떻게 해결할까 고민을 하다가 조인을 통해서 해결할 수 있었다.

서브쿼리로 해결하기

다짜고짜 쿼리를 살펴보자

SELECT
	`store_indexholder` AS `store_number`,
	`store_name`, 
   	`vote_grade_average`, 
	`vote_grade_count`, 
   	`store_id`, 
	IF((SELECT `created_at` 
    	    FROM `starred_store` AS ss 
 	    WHERE `member_id` = ? 
            AND `ss`.`store_id` = `si`.`store_id` 
            AND `is_visible` = 1 
            LIMIT 1) IS NOT NULL, TRUE, FALSE) AS `starred`
FROM `store_information` AS `si`
WHERE `is_visible` = 1
ORDER BY `vote_grade_average` DESC

가독성이 개박살나버렸다. 쿼리의 의도를 설명하자면 다음과 같다.

  • is_visible = 1store_informaion 테이블의 Row를 불러온다.
  • 서브쿼리를 날려서 is_visible = 1 이고 store_id 가 메인쿼리와 동일한 starred_store 를 불러온다.
  • 서브쿼리의 결과가 NULL이면 FASLE이고 NULL이 아니면 TRUE를 반환한다.

이 SQL을 작성하면서도 너무 마음에 안들었던건 가독성이다. 그리고 뭔가 너무 비효율적으로 보였다.

조인으로 해결하기

쿼리를 살펴보자

SELECT si.store_image, si.store_indexholder, si.store_name, 
       si.store_intro, si.vote_grade_count, si.vote_grade_average, 
       IF (ss.store_id IS NOT NULL, TRUE, FALSE) AS starred 
FROM `store_information` AS si 
LEFT JOIN `starred_store` AS ss 
ON si.store_id = ss.store_id AND ss.is_visible = 1  
WHERE si.store_id = ?

LEFT JOIN 을 사용하니까 가져오는 컬럼의 수는 7개로 동일하지만 쿼리가 매우 짧아졌다. 어째서 서브쿼리를 대체할 수 있었을까?

  • 조회하고자 하는 조건의 데이터는 항상 하나의 Row만 가지고 있다.
  • LEFT JOIN 은 기준이 되는 테이블 (지금 사례에서는 store_information)은 무조건 출력하고 기준이 아닌 테이블이 비어있으면 컬럼에 NULL을 채워준다.

위의 두 가지 조건으로 인해 대체할 수 있었다. 내가 원하는 조건의 데이터가 없을 때는 NULL을 FALSE로 바꾸고 싶었는데 마침 LEFT JOIN은 기준이 되는 테이블은 그대로 출력하지만 기준이 되지 않는 녀석은 NULL로 채워서 준다. 만세!!

성능 측정

그렇다면 두 녀석의 성능은 얼마나 차이날까? 사실 별 차이 안난다. MySQL의 캐시기능 때문인지 0.000초가 걸릴 때도 있고 0.016초가 걸린다. (두 쿼리 모두) 그래도 가독성이 좋아진걸로 만족한다.

profile
노력하는 자는 즐기는 자를 이길 수 없다 를 알면서도 게으름에 지는 중

0개의 댓글