작성
·
158
·
수정됨
0
[질문 템플릿]
1. 강의 내용과 관련된 질문인가요? (예/아니오)
2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)
3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)
1. 2. 3. 예.
[질문 내용]
SELECT
절에서 상관 서브쿼리를 사용하는 것에 대해, 완전히 다른 테이블에서 간단한 정보 하나만 조회할 때 적재적소에 사용하면 괜찮다고 하셨는데
상황
저희 회사에서 사용하는 쿼리들을 보면, SELECT
절에서 상관 서브쿼리를 활용해 다른 테이블에서 간단한 정보를 조회하는 경우가 많습니다.
예를 들어, 작성자, 요청자, 접수자 각각에 대해 이름과 부서명을 조회할 때, 각 항목마다 서브쿼리가 사용됩니다.
작성자 이름: (SELECT name FROM EMP WHERE emp_id = main.writer_id)
작성자 부서: (SELECT dept_name FROM DEPT WHERE dept_id = main.writer_dept_id)
요청자 이름, 접수자 이름 등도 동일한 방식
질문
이렇게 PK 기준으로 조건을 걸고 조회하는 서브쿼리가 여러개 들어가도 성능상 괜찮을까요?
조인으로 처리하면 조인 테이블이 많아져 쿼리가 복잡해질 것 같은데. 이럴 경우 서브쿼리 방식이 더 적절한가요?
만약 이 방식이 성능상 좋지 않다면, 보다 나은 방법이나 현실적인 대안이 있을까요?
답변 2
4
안녕하세요. 개발자는개발님
사실 저도 주니어 시절에 참 많이 했던 고민인데요. 우선 데이터베이스 안에서의 해결 방안과 그 다음으로 더 시야를 넓혀서 애플리케이션 레벨의 해결 방안도 함께 보도록 하겠습니다.
1. 여러 개의 PK 기반 서브쿼리, 성능상 괜찮을까요?
상황에 따라 다릅니다. 괜찮을 수도, 심각한 성능 저하를 일으킬 수도 있습니다.
괜찮은 경우 (소량 데이터)
메인 쿼리가 조회하는 로우(row)의 수가 적을 때 (예: 수십 ~ 수백 건)
서브쿼리의 WHERE
절 조건이 PK나 Unique Index로 걸려있을 때
이유는 간단합니다. 상관 서브쿼리는 메인 쿼리에서 조회된 모든 로우에 대해 각각 한 번씩 실행됩니다. 예를 들어 메인 쿼리 결과가 100건이고, 서브쿼리가 6개 있다면 100 * 6 = 600
번의 추가적인 쿼리가 실행되는 셈입니다. 하지만 PK 조회는 매우 빠르기 때문에, 전체 로우 수가 적다면 눈에 띄는 성능 저하를 느끼지 못할 수 있습니다. 따라서 이런 경우는 크게 문제가 되지는 않습니다.
성능 저하가 발생하는 경우 (대량 데이터)
메인 쿼리가 조회하는 로우의 수가 많을 때 (예: 수천 건 이상)
만약 메인 쿼리 결과가 100,000건이라면 100,000 * 6 = 600,000
번의 서브쿼리가 실행됩니다. 아무리 PK 조회가 빠르다고 해도, 이 정도의 반복 실행은 데이터베이스에 상당한 부하를 주며 쿼리 속도를 급격히 저하시킵니다.
결국 데이터를 얼마나 보는지 그 양에 따라 성능이 달리집니다.
2. 서브쿼리 vs JOIN: 가독성과 복잡성
말씀하신 대로, JOIN 테이블이 많아지면 쿼리가 길어지고 복잡해 보이는 것은 사실입니다.
서브쿼리 방식:(SELECT name FROM EMP WHERE emp_id = main.writer_id)
처럼, 필요한 값을 직관적으로 가져오는 것처럼 보여 가독성이 좋다고 느낄 수 있습니다.
JOIN 방식:FROM
절이 길어지고, SELECT
절에서 writer.name
, writer_dept.dept_name
처럼 별칭(alias)을 사용해야 해서 초기에는 복잡하게 보일 수 있습니다.
하지만 관계형 데이터베이스는 개별 데이터를 하나씩 처리하는 것보다 여러 테이블을 연결해 '집합(Set)' 단위로 한 번에 처리하는 JOIN 연산에 더 최적화되어 있습니다. 최신 데이터베이스의 옵티마이저(Optimizer)는 여러 개의 JOIN이 포함된 복잡한 쿼리라도 최적의 실행 계획(Execution Plan)을 찾아 효율적으로 처리합니다.
따라서 복잡해 보이더라도 성능과 확장성을 고려하면 JOIN 방식이 더 적절한 선택입니다.
3. 현실적인 대안 및 권장 방안
현재 운영 중인 시스템에서 성능 이슈가 없다면 기존 쿼리를 모두 수정할 필요는 없습니다.
메인 쿼리가 조회하는 로우 수가 적다면 PK를 기반으로 하는 서브 쿼리 조회가 성능상 크게 문제가 되지는 않습니다.
하지만 성능이 느린 쿼리를 개선할 때는 LEFT JOIN
방식을 우선적으로 고려하세요.
특히, 조회되는 데이터의 양이 많아질 가능성이 있는 쿼리라면 처음부터 JOIN
으로 설계하는 것이 장기적으로 유리합니다.
애플리케이션 캐시 활용 지원
지금까지 설명한 내용은 데이터베이스 안에서의 해결 방법입니다. 여기서 시야를 좀 더 넓혀서 만약 애플리케이션 레벨에서 캐시를 사용할 수 있다면 여기서 아주 많은 부분을 크게 개선할 수 있습니다. 예를 들어서 작성자 정보, 요청자 부서 같은 데이터가 그렇게 많지 않고, 잘 변경되지는 않는다고 가정하겠습니다. 이런 경우 애플리케이션 레벨에서 캐시를 사용할 수 있다면 작성자, 요청사, 부서 같은 정보들을 애플리케이션 로딩 시점에 캐시에 보관하는 것입니다.
DB에서는 작성자ID, 요청자 ID, 부서 ID 같은 값만 조회하고, 애플리케이션에서 필요할 때 캐시에서 조회하는 것이지요. 이렇게 하면 데이터베이스 조회 쿼리를 조인이나 서브 쿼리 없이 매우 단순화 할 수 있고, 나머지는 애플리케이션에서 캐시에서 조회하면 됩니다.
이런 방식은 특히 코드성 데이터를 조회할 때 큰 도움이 됩니다.
코드성 데이터의 경우 SQL에서 코드의 값을 조회하기 위해 테이블과 조인하거나 서브 쿼리 쿼리를 상당히 많이 사용해야 합니다. 결과적으로 SQL이 매우 지저분해지고 알아보기가 힘들어집니다.
이런 코드성 테이블을 애플리케이션 로딩 시점에 캐시에 보관할 수 있다면 SQL에서는 코드 테이블을 조인할 필요 없이 필요한 데이터만 조회하고, 코드의 값은 애플리케이션에서 캐시에 보관된 데이터를 필요할 때만 조회하면 됩니다.
물론 캐시과 데이터베이스의 동기화에 대한 부분은 고민을 해야 합니다. 가장 단순한 방법은 메모리에 캐시를 로딩하고 1~10분 정도의 TTL을 가져가는 방법 등이 있을 수 있습니다.
도움이 되셨길 바래요 🙂
감사합니다.
안녕하세요. dev.rudevico님
생각하신 내용이 맞습니다 🙂
데이터도 적고, 변경되는 일도 상대적으로 드물고, 조회는 너무 자주 일어나기 때문에 캐싱해서 사용하기에 최적의 데이터가 됩니다.
감사합니다.
0
안녕하세요, 인프런 AI 인턴입니다. 개발자는개발님!
SELECT
절에서 상관 서브쿼리를 사용하는 것은 간단한 정보를 조회할 때 유용할 수 있지만, 여러 개의 서브쿼리를 쓴다면 성능 문제나 가독성에서 아쉬움이 있을 수 있습니다. 질문에 대한 답변을 드리겠습니다.
성능 문제: 상관 서브쿼리를 여러 개 사용하면 쿼리 실행마다 별도의 서브쿼리를 실행해야 하므로 성능에 부담이 될 수 있습니다. 특히, 각 서브쿼리는 독립적으로 실행되어 성능상 비효율적일 수 있습니다.
조인과의 비교: 여러 테이블과 조인을 사용하면 SQL은 한 번의 쿼리 실행으로 필요한 데이터를 가져오므로 더 효율적으로 작동할 수 있습니다. 다만 문의하신 대로 조인 테이블이 많아져 쿼리가 복잡해질 수 있는데, 이 경우 쿼리 계획을 잘 세워서 필요한 컬럼만을 선택적으로 사용하는 방법 등으로 복잡성을 줄일 수 있습니다.
대안 방법: 조인 또는 일부 권장되는 방식은 뷰(View)를 사용하는 것입니다. 뷰를 사용해서 재사용 가능한 사전 구축된 결과 집합을 미리 정의하면, 복잡한 조인 로직을 깨끗하고 재사용 가능한 방식으로 관리할 수 있습니다. 또한, 미리 정의된 뷰는 가독성에서도 유리하며 성능상 이점도 있을 수 있습니다.
아래 유사한 질문과 답변을 확인해 보시면 도움이 될 것입니다:
이런 방법들을 참고하여 성능을 개선하고, 가독성을 향상시키는 방법을 고민해 보시기를 추천드립니다.
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다. 현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏 추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.
안녕하세요, 영한님. 답변에서 언급하신
코드성 테이블
과 관련해서 질문이 있습니다!먼저, 코드성 테이블이란 다음 사진과 같이
코드-코드 값
쌍으로 데이터를 저장하는 테이블이라고 이해했습니다.이때 "코드성 데이터를 조회할 때 캐싱이 특히 도움이 된다"고 말씀하신 이유에 대해 다음과 같이 해석했는데, 제 해석이 적절한지 여쭙고 싶네요!
코드성 테이블의 rows는 늘어날 가능성이 적고, 늘어난다고 하더라도 그 수가 많지 않다.
예를 들어, 고객 등급에 대한 코드성 테이블의 경우
(코드, 코드 값) = (V, VIP 등급), (G, Gold 등급), (S, Silver 등급), ...
으로 구성이 되는데 시간이 지나 고객 등급의 종류가 추가되더라도 그 수가 10개를 넘지 않을 가능성이 크다.(결국 고객 등급에 대한 코드성 테이블의 경우 rows가 계속 적음이 보장되고, 조회는 주문할 때마다 할인율 계산 등에 의해 여러 번 발생할 것이므로) 이를 애플리케이션 레벨에서 캐시로 저장하면 RAM 용량에 대한 부담도 거의 없을 뿐더러, DB 접근 횟수를 크게 줄여 성능상 이점을 얻게 된다.
감사합니다.
사진 및 예시 출처: https://cskstory.tistory.com/entry/코드-테이블-설계하기