SQL 기본문법 > 서브쿼리(SubQuery) - 단일행/복수행 서브쿼리
24 Jul 2025 | SQL개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.
서브쿼리(SubQuery)
SQL문 내부에서 사용하는 SELECT문
서브쿼리는 WHERE, SELECT, FROM, HAVING절 안에 넣어 사용할 수 있고, 이러한 서브쿼리는 괄호안에 적어주어야 한다. 일반적으로 서브쿼리가 먼저 실행된 후 메인쿼리가 실행되는 순서로 작업이 이루어진다.
서브쿼리와 조인의 차이점
- 데이터 추출 접근 방식이 다름
- 서브쿼리는 한문장 안에 1개의 메인쿼리와 1개 이상의 서브쿼리가 있는 구조
- 조인은 1개의 쿼리 안에 2개 이상의 테이블을 연결한 후 컬럼을 조회
간단한 쿼리 혹은 간단한 필터링 작업에는 서브쿼리가 좋겠지만, 쿼리가 복잡하거나 여러테이블간의 관계를 처리해야할 경우에는 조인을 사용하는 것이 더 좋다.
이러한 서브쿼리의 종류는 크게 두개로 구분된다.
- 단일 행 서브쿼리: 서브쿼리의 결과가 단일행이 나오는 쿼리
- 복수 행 서브쿼리: 서브쿼리의 결과가 어러행이 나오는 쿼리
단일 행 서브쿼리
서브쿼리의 결과가 단일행이 나오는 쿼리
=, >, <, >=, <=
같은 비교연산자를 통해 메인쿼리와 서브쿼리를 연결한다.
-- 문제: 최고 마일리지를 보유한 고객의 정보를 보이시오
SELECT 고객번호, 고객회사명, 담당자명, 마일리지 FROM 고객
WHERE 마일리지 = (SELECT MAX(마일리지) FROM 고객);
-- 사실 위에 문제는 아래와같이도 풀 수 있다.
SELECT 고객번호, 고객회사명, 담당자명, 마일리지 FROM 고객
ORDER BY 마일리지 desc LIMIT 1;
문제를 더 살펴보자.
-- 문제1: 주문번호 'H0250'을 주문한 고객에 대해 고객회사명과 담당자명을 보이시오
SELECT 고객회사명, 담당자명 FROM 고객
WHERE 고객번호 = (SELECT 고객번호 FROM 주문 WHERE 주문번호 = 'H0250');
-- 이걸 조인으로 풀어보자
SELECT 고객회사명, 담당자명 FROM 고객
INNER JOIN 주문 ON 고객.고객번호 = 주문.고객번호
WHERE 주문번호 = 'H0250';
-- 문제2: 부산광역시 고객의 최소 마일리지보다 더 큰 마일리지를 가진 고객 정보를 보이시오
SELECT 담당자명, 고객회사명, 마일리지 FROM 고객
WHERE 마일리지 > (SELECT MIN(마일리지) FROM 고객 WHERE 도시 = '부산광역시');
이렇듯 비교연산자들을 통해서도 서브쿼리 구문을 만들어줄 수 있다.
복수 행 서브쿼리
서브쿼리의 결과가 여러행이 나오는 쿼리
IN, ALL, ANY, SOME, EXISTS
같은 비교연산자를 통해 메인쿼리와 서브쿼리를 연결한다.
- IN: 메인쿼리의 비교조건이 서브쿼리 결과 중 하나라도 일치하면 참(
=
연산자로 비교) - ALL: 서브쿼리의 각 결과값을 비교연산자로 비교해서 모두 일치해야 참(
>, <, >=, <=
) - ANY, SOME: 서브쿼리의 각 결과값을 비교연산자로 비교해서 하나라도 일치하면 참(
>, <, >=, <=
) - EXISTS: 서브쿼리에 비교조건을 만족하는 결과가 존재하면 참 (행의 존재여부로 비교)
-- 문제1: 부산광역시 고객이 주문한 주문건수를 보이시오
SELECT COUNT(*) 주문건수 FROM 주문
WHERE 고객번호 IN (SELECT 고객번호 FROM 고객 WHERE 도시 = '부산광역시');
-- 문제2: 부산광역시 고객의 마일리지들보다 마일리지가 큰 고객이 한명이라도 있는 정보를 보이시오
SELECT 담당자명, 고객회사명, 마일리지 FROM 고객
WHERE 마일리지 > ANY(SELECT 마일리지 FROM 고객 WHERE 도시 = '부산광역시');
-- 문제3: 각 지역의 어느 평균 마일리지보다도 마일리지가 큰 고객의 정보를 보이시오
SELECT 담당자명, 고객회사명, 마일리지 FROM 고객
WHERE 마일리지 > ALL (SELECT AVG(마일리지) FROM 고객 GROUP BY 지역);
-- 문제4: 한번이라도 주문한 적 있는 고객의 정보를 보이시오
SELECT 고객번호, 고객회사명 FROM 고객
WHERE EXISTS (SELECT 고객번호 FROM 주문);
-- 위 문제를 IN을 사용해본다면?
SELECT 고객번호, 고객회사명 FROM 고객
WHERE 고객번호 IN (SELECT 고객번호 FROM 주문);
-- 위 문제를 JOIN을 사용해본다면?
SELECT DISTINCT 고객.고객번호, 고객회사명 FROM 고객
INNER JOIN 주문 ON 고객.고객번호 = 주문.고객번호 ORDER BY 1
INNER JOIN의 경우 교집합만 가져오다보니, 주문을 한 고객번호만 조회될 것이다. 그렇기 때문에 특별한 조건문이 더이상 필요하지는 않다. 고객번호순으로 정렬한 뒤, 번호와 회사명은 여러개 보여줄 필요가 없으니 중복을 제거해주는 DISTINCT를 사용해준다.