상관 하위 쿼리를 JOIN으로 다시 작성하기
성능 향상을 위해 상관 논리를 JOIN이나 윈도 함수로 평탄화합니다.
상관 하위 쿼리를 JOIN으로 다시 작성하기은(는) CoddyKit의 무료 Coding Interview Prep 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Coding Interview Prep 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Coding Interview Prep 강의에는 총 4개의 강의가 포함되어 있습니다.
왜 다시 작성해야 할까요
상관 서브쿼리는 읽기 쉽지만 느릴 수 있습니다. 내부 쿼리가 외부의 각 행마다 한 번씩 실행될 수 있기 때문입니다. 면접관은 성능을 높이기 위해 이를 조인 또는 윈도 함수로 다시 작성하라고 자주 요구합니다.
목표는 내부 검색을 반복하는 대신 데이터를 한 번 순회하여 동일한 결과를 얻는 것입니다.
두세 가지 다시 작성 패턴과 각 패턴이 언제 정확성을 유지하는지 아는 것은 중급 개발자에게 필요한 핵심 역량입니다.
패턴 1: EXISTS에서 INNER JOIN으로
최소 하나의 일치 항목이 있는지 검사하는 상관 EXISTS는 흔히 INNER JOIN으로 바꿀 수 있습니다.
하지만 여러 내부 행이 일치하면 조인이 외부 행을 중복해서 만들 수 있으므로 주의하십시오. 외부 키당 한 행으로 되돌리려면 DISTINCT를 추가하거나 집계하십시오.
-- Correlated EXISTS
SELECT c.customer_id, c.name
FROM customers c
WHERE EXISTS (SELECT 1 FROM orders o
WHERE o.customer_id = c.customer_id);
-- Join rewrite (DISTINCT avoids dupes from fan-out)
SELECT DISTINCT c.customer_id, c.name
FROM customers c
JOIN orders o ON o.customer_id = c.customer_id;팬아웃 함정
가장 흔한 다시 작성 오류는 팬아웃을 잊는 것입니다. EXISTS는 주문이 몇 개이든 각 고객을 한 번만 반환합니다. 단순한 조인은 주문당 한 행을 반환하여 개수를 부풀립니다.
후속 단계에서 조인 결과에 대해 신중하게 그룹화하지 않고 COUNT(*) 또는 SUM(amount)를 수행하면 숫자가 잘못됩니다.
항상 조인이 행을 늘릴 수 있는지 확인하십시오. 그렇다면 DISTINCT 또는 GROUP BY를 사용해 다시 하나로 합치십시오.
패턴 2: NOT EXISTS에서 LEFT JOIN / IS NULL로
안티 조인으로 다시 작성하는 방식은 면접에서 반드시 알아 두어야 할 패턴입니다. 상관 NOT EXISTS는 오른쪽이 NULL인 LEFT JOIN으로 바꿀 수 있습니다.
일치하지 않는 외부 행은 오른쪽에 NULL을 얻으므로, 해당 NULL을 기준으로 필터링하면 일치 항목이 없는 행만 정확하게 남습니다.
-- Correlated NOT EXISTS
SELECT c.customer_id FROM customers c
WHERE NOT EXISTS (SELECT 1 FROM orders o
WHERE o.customer_id = c.customer_id);
-- LEFT JOIN / IS NULL rewrite
SELECT c.customer_id
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.customer_id
WHERE o.customer_id IS NULL;검사할 NULL이 아닌 열 선택
LEFT JOIN / IS NULL 방식으로 다시 작성할 때는 실제로 일치하는 경우 절대 NULL이 되지 않는 오른쪽 열을 검사하십시오. 가장 좋은 선택은 조인 키 또는 기본 키입니다.
NULL을 허용하는 열을 검사하면 실제로 일치하지 않는 경우(행이 없음)와 일치하는 행에 해당 열의 값이 단순히 NULL인 경우를 구분할 수 없습니다. 이 오류로 잘못된 행이 반환됩니다.
조인 키인 o.customer_id 또는 o.order_id를 사용하면 NULL이 "일치하는 행이 없음"을 의미하도록 보장할 수 있습니다.
패턴 3: 스칼라 집계에서 JOIN + GROUP BY로
SELECT의 상관 집계는 그룹화된 서브쿼리(파생 테이블)와의 조인으로 바꿀 수 있습니다.
그룹별 집계 결과를 한 번 계산한 다음 상세 행에 다시 조인하십시오. 그러면 내부 쿼리가 행마다 실행되지 않고 한 번만 실행됩니다.
-- Correlated scalar aggregate
SELECT e1.name,
(SELECT MAX(e2.salary) FROM employees e2
WHERE e2.dept_id = e1.dept_id) AS dept_max
FROM employees e1;
-- Join + GROUP BY rewrite
SELECT e.name, m.dept_max
FROM employees e
JOIN (SELECT dept_id, MAX(salary) AS dept_max
FROM employees GROUP BY dept_id) m
ON m.dept_id = e.dept_id;패턴 4: 윈도 함수로 다시 작성하기
가장 깔끔한 다시 작성 방식은 윈도 함수인 경우가 많습니다. MAX(salary) OVER (PARTITION BY dept_id)를 사용하면 상관 집계를 완전히 대체할 수 있으며 조인이 필요하지 않습니다.
이 방식은 그룹 값을 한 번의 순회로 계산하면서 모든 상세 행을 유지합니다. 분석 쿼리에서 면접관이 가장 보고 싶어 하는 답인 경우가 많습니다.
SELECT name,
dept_id,
salary,
MAX(salary) OVER (PARTITION BY dept_id) AS dept_max
FROM employees;그룹별 상위 N개로 다시 작성하기
그룹별 최상위 행을 선택하는 상관 서브쿼리(salary = MAX per dept)는 ROW_NUMBER를 사용하면 깔끔하게 다시 작성할 수 있습니다.
그룹별로 분할하고 지표를 기준으로 정렬한 다음 순위가 1인 행만 남기십시오. 동률인 최상위 행을 모두 원한다면 RANK를 대신 사용하십시오.
SELECT name, dept_id, salary
FROM (
SELECT name, dept_id, salary,
ROW_NUMBER() OVER (PARTITION BY dept_id
ORDER BY salary DESC) AS rn
FROM employees
) t
WHERE rn = 1;다시 작성하지 말아야 할 때
다시 작성한다고 항상 더 좋아지는 것은 아닙니다. 다음과 같은 경우에는 상관 서브쿼리를 유지하십시오.
- 외부 집합이 매우 작아서 행별 비용이 무시할 수 있을 정도인 경우
- 상관 열에 적절한 인덱스가 있고 최적화기가 이미 효율적인 세미 조인으로 변환하는 경우
- 유지 관리하는 코드에서 미세한 최적화보다 가독성이 중요한 경우
최신 최적화기는 EXISTS를 세미 조인으로 자동 변환하는 경우가 많습니다. 다시 작성하면 도움이 된다고 가정하기 전에 EXPLAIN으로 측정하겠다고 말하십시오.
동등성 확인
어떤 방식으로 다시 작성한 뒤에는 원래 쿼리와 동일한 행과 동일한 행 수를 반환하는지 확인하십시오.
- 행 수가 일치하는지 확인합니다.
- 조인 팬아웃으로 중복이 생기지 않았는지 확인합니다.
- NULL과 빈 그룹의 경계 사례가 여전히 올바르게 동작하는지 확인합니다.
빠른 방법은 두 버전을 실행한 뒤 양쪽 방향으로 EXCEPT를 수행하는 것입니다. 빈 결과가 나오면 두 결과가 일치한다는 뜻입니다. 면접관은 추측하지 않고 검증하는 태도를 높이 평가합니다.
SELECT customer_id FROM query_a
EXCEPT
SELECT customer_id FROM query_b;
-- and the reverse; both empty => equivalentIN을 JOIN으로 다시 작성하기
비상관 IN 서브쿼리도 흔히 조인으로 다시 작성할 수 있지만, 같은 팬아웃 주의 사항이 적용됩니다. IN은 포함 여부를 확인할 때 중복을 제거하지만 조인은 그렇지 않습니다.
내부 목록에 중복 키가 있으면 조인이 외부 행을 반복합니다. IN의 의미와 일치시키려면 내부 쪽 또는 최종 결과에 DISTINCT를 사용하십시오.
-- IN subquery
SELECT c.name FROM customers c
WHERE c.customer_id IN (SELECT o.customer_id FROM orders o);
-- Join rewrite, de-duplicated to match IN
SELECT DISTINCT c.name
FROM customers c
JOIN orders o ON o.customer_id = c.customer_id;빠른 확인
상관 NOT EXISTS 안티 조인에 맞는 올바른 조인 다시 작성 방식을 선택하십시오.
요약: 상관 서브쿼리를 조인으로 다시 작성하기
핵심 내용:
EXISTS→INNER JOIN(팬아웃으로 인한 중복을 피하려면 DISTINCT를 추가합니다.)NOT EXISTS→LEFT JOIN ... WHERE key IS NULL(NULL이 될 수 없는 열을 검사합니다.)- 상관 스칼라 집계 → 그룹화된 파생 테이블을
JOIN하거나, 더 나은 방법으로 윈도 함수를 사용합니다. - 그룹별 상위 행 →
ROW_NUMBER(동률에는RANK) - 다시 작성한 결과가 동등한지 확인하고, 더 빠르다고 가정하기 전에
EXPLAIN으로 검사합니다.
두 방식과 팬아웃 함정을 모두 아는지가 바로 중급 면접에서 확인하는 내용입니다.
AI 튜터와 함께 Coding Interview Prep을(를) 배우세요 — 무료
브라우저에서 실제 코드를 작성하고 실행하며, 24/7 AI 튜터로부터 즉각적인 도움을 받고, 웹이나 앱에서 중단한 부분부터 계속 학습하세요.
- 코스
- 90
- 레슨
- 360
자주 묻는 질문
“상관 하위 쿼리를 JOIN으로 다시 작성하기” 강의는 무료인가요?
네 — “상관 하위 쿼리를 JOIN으로 다시 작성하기” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Coding Interview Prep 강의 전체를 잠금 해제할 수 있습니다. Coding Interview Prep 강의에는 총 4개의 강의가 포함되어 있습니다.
“상관 하위 쿼리를 JOIN으로 다시 작성하기”에서 뭘 배우나요?
성능 향상을 위해 상관 논리를 JOIN이나 윈도 함수로 평탄화합니다. 브라우저에서 직접 실행하는 실습 코드로 Coding Interview Prep을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
Coding Interview Prep을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 Coding Interview Prep은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.
“상관 하위 쿼리를 JOIN으로 다시 작성하기” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 Coding Interview Prep 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 Coding Interview Prep 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- 상관 하위 쿼리의 구조
- GROUP BY 없이 그룹별 집계하기
- 상관 EXISTS와 NOT EXISTS
- 상관 하위 쿼리를 JOIN으로 다시 작성하기