데이터 추출을 위해 SQL 구문을 작성하다보면, 잦은 빈도로 서브쿼리를 사용하게 됩니다. “서브쿼리는 실행 속도가 느리다던데..” 막연하게 알고 계신가요? 네, 저도 그랬어요 😇 그래서 공부하고 정리해 보았습니다. 서브쿼리가 전체 성능에 어떤 영향을 미치는지, 어떤 대안을 활용할 수 있는지 함께 알아보아요.
서브쿼리는 괄호 안에 완전한 SELECT 문을 넣어 생성한 테이블 표현식이에요. 쉽게 말하면 쿼리 안에 또 다른 쿼리가 들어 있는 것이지요. 통상 FROM 혹은 JOIN 구문에 서브쿼리를 사용하는 경우가 많을 텐데요. 이 외에도 서브쿼리의 쓰임새는 광범위 합니다. 아래서 몇 가지 활용 예시를 소개 합니다.
테이블 서브쿼리
단일 컬럼 테이블 서브쿼리
스칼라 서브쿼리
이토록 유연하고 다양한 기능을 제공하는 서브쿼리, 왜 문제가 되는 걸까요? 가장 큰 문제는 성능 저하입니다. 서브쿼리는 테이블과 같은 형태의 결과 값을 만들어 내지만, 실제 데이터를 저장하지는 않기 때문에 성능 저하를 야기할 수 있어요.
연산 비용 추가
데이터 I/O 비용 발생
최적화를 받을 수 없음
서브쿼리의 또 다른 단점은 가독성입니다. 코드가 여러 계층에 걸쳐 만들어 지면서 가독성이 떨어지고, 가독성이 낮은 쿼리는 유지, 관리가 어렵습니다. 비단 SQL 쿼리, 그리고 서브쿼리에 국한된 내용은 아니지만 가독성 좋은 코드에 대한 몇 가지 좋은 글을 함께 공유 합니다.
특정 테이블에 대한 접근 횟수가 많을 수록, 데이터를 읽고 쓰는 비용이 늘어나요! 그러니 쿼리를 (더 좋은 방향으로) 수정할 때는 여러 번 호출된 테이블의 접근 횟수를 줄일 수 있는지 생각해 보아요. 아래 WINDOW 함수 활용 예시를 보면, Receipts 테이블에 대한 접근 횟수를 2회에서 1회로 줄인 것을 알 수 있어요.
무분별한 서브쿼리 사용의 또 다른 폐해는 가독성이 떨어진다는 점이었죠. 이 점을 보완하기 위해서는 CTE(Common Table Expression)을 활용하면 좋습니다. 유사한 패턴의 서브쿼리를 여러 번 반복적으로 사용하는 경우, WITH 구문 사용 여부의 차이가 더 두드러지게 나타나요.
CTE를 통해 만들어지는 결과 값 또한 임시 테이블의 일종이기 때문에, 쿼리 성능을 고려해서 꼭 필요한 데이터만 필터할 것을 권장합니다. CTE 구문의 모든 컬럼은 다른 쿼리에서 사용되지 않는 경우에도 연산되니 주의하세요!
위에서 소개한 여러 해결책을 코드를 작성하는 첫 단계부터 적용하기란 쉽지 않습니다. 외려, 처음 쿼리를 고민할 때는 먼저 서브쿼리를 사용하면서 연산의 논리를 전개하는 게 일의 속도와 효율에 도움이 될 수 있어요. 그러니 처음부터 기능과 성능이 모두 우수한 쿼리를 작성하려고 끙끙대기 보다, 빠르게 만들들고 성능을 고려해서 튜닝 하기를 권장합니다. 반복적으로 활용되는 쿼리라면 더더욱이요!