서버를 개발하다가 외부 테이블에 특정한 조건에 해당하는 로우가 존재하는지 체크해야할 필요가 있었다. 그 로직을 처음에는 서브쿼리를 통해서 해결을 했는데 알고보니 서브쿼리는 왠만하면 사용을 하지 말라고 하더라(나중에 왜 사용하면 안되는지 정확히 알아봐야겠다) 그래서 어떻게 해결할까 고민을 하다가 조인을 통해서 해결할 수 있었다.
다짜고짜 쿼리를 살펴보자
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 = 1
인 store_informaion
테이블의 Row를 불러온다.is_visible = 1
이고 store_id
가 메인쿼리와 동일한 starred_store
를 불러온다.이 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개로 동일하지만 쿼리가 매우 짧아졌다. 어째서 서브쿼리를 대체할 수 있었을까?
LEFT JOIN
은 기준이 되는 테이블 (지금 사례에서는 store_information
)은 무조건 출력하고 기준이 아닌 테이블이 비어있으면 컬럼에 NULL을 채워준다.위의 두 가지 조건으로 인해 대체할 수 있었다. 내가 원하는 조건의 데이터가 없을 때는 NULL을 FALSE로 바꾸고 싶었는데 마침 LEFT JOIN은 기준이 되는 테이블은 그대로 출력하지만 기준이 되지 않는 녀석은 NULL로 채워서 준다. 만세!!
그렇다면 두 녀석의 성능은 얼마나 차이날까? 사실 별 차이 안난다. MySQL의 캐시기능 때문인지 0.000초가 걸릴 때도 있고 0.016초가 걸린다. (두 쿼리 모두) 그래도 가독성이 좋아진걸로 만족한다.