쿼리 안에 또 다른 쿼리, 서브쿼리
서브쿼리의 특징
1. 반드시 소괄호로 감싸 사용
2. 주 쿼리를 실행하기 전에 1번만 실행
3. 비교 연산자와 함께 사용하는 경우, 서브 쿼리를 연산자 오른쪽에 기술
4. 서브 쿼리 내부에는 ORDER BY문을 사용할 수 없음
서브쿼리의 활용
- 조인하지 않은 상태에서 다른 테이블과 일치하는 행 찾기
- 조인 결과를 다시 조인할 때 사용
WHERE 문에 서브 쿼리 사용하기
WHERE문에 사용하는 서브 쿼리를 중첩 서브 쿼리라고 함
- 중첩 서브 쿼리는 WHERE 다음에 오는 조건문의 일부로 사용
- (또 다른 SELECT문을 사용한 결과)를 주 쿼리의 조건값으로 사용
✔서브 쿼리를 비교 연산자와 함께 사용할 때는 반드시 서브 쿼리의 반환 결과가 1건이라도 있어야 함
- 만약 반환 결과가 2건 이상인 경우에는 다중 행 연산자 (IN, ALL 등)를 사용
- 다중 행 연산자 ?
- 서브 쿼리의 결과가 2건 이상인 것을 다중 행이라고 함

단일 행 서브 쿼리 사용하기
단일 행 서브 쿼리 : 서브 쿼리의 결과로 1행만 반환되는 쿼리
[기본 형식]
SELECT 열
FROM 테이블
WHERE 열 = (
SELECT 열
FROM 테이블
)
EX1)
SELECT *
FROM customer
WHERE customer_id = (
SELECT customer_id
FROM customer
WHERE first_name = 'ROSA'
);
WHERE 문에 서브 쿼리를 걸어서 first_name = ‘ROSA’인 데이터의 customer_id를 추출해 조건으로 사용한 것
다중 행 서브 쿼리 사용하기
다중 행 서브 쿼리 : 서브 쿼리 결과로 2행 이상 반환되는 쿼리
[ IN 연산자를 활용한 다중 행 서브 쿼리 ]
SELECT 열
FROM 테이블
WHERE 열 IN (
SELECT 열
FROM 테이블
)
EX1)
SELECT *
FROM customer
WHERE first_name IN ('ROSA', 'ANA');
EX2)
SELECT *
FROM customer
WHERE customer_id IN (
SELECT customer_id
FROM customer
WHERE first_name IN ('ROSA', 'ANA')
);
>> 두 쿼리의 실행 결과는 같다
[ 테이블 3개를 조인하는 쿼리 ]
SELECT a.film_id, a.title
FROM film AS a
INNER JOIN film_category AS b ON a.film_id = b.film_id
INNER JOIN category AS c ON b.category_id = c.category_id
WHERE c.name = 'Action';
[ 테이블 3개 조인 - IN을 활용한 서브 쿼리 ]
SELECT film_id, title
FROM film
WHERE film_id IN (
SELECT a.film_id
FROM film_category AS a
INNER JOIN category AS b ON a.category_id = b.category_id
WHERE b.name = 'Action'
);
- WHERE 문에 사용한 서브 쿼리 안에서 테이블 2개를 조인한 결과를 IN 소괄호 안에 작성
[ NOT IN을 활용한 서브 쿼리 ]
SELECT film_id, title
FROM film
WHERE film_id NOT IN (
SELECT a.film_id
FROM film_category AS a
INNER JOIN category AS b ON a.category_id = b.category_od
WHERE b.name = 'Action'
);
[ = ANY를 활용한 서브 쿼리 ]
SELECT *
FROM customer
WHERE customer_id = ANY (
SELECT customer_id
FROM customer
WHERE first_name IN ('ROSA', 'ANA')
);
- = ANY : 서브 쿼리 결과에서 값이 하나라도 만족하면 데이터 조회 가능
- 즉, 서브쿼리 결괏값이 여러 개여도 일치하는 모든 행을 주 쿼리에서 조회 가능
[ <ANY를 활용한 서브 쿼리 ]
SELECT *
FROM customer
WHERE customer_id = < ANY (
SELECT customer_id
FROM customer
WHERE first_name IN ('ROSA', 'ANA')
);
- < ANY : 서브 쿼리의 결과값보다 작은 값을 조건으로 하여 데이터 반환
[ > ANY를 활용한 서브 쿼리 ]
SELECT *
FROM customer
WHERE customer_id = < ANY (
SELECT customer_id
FROM customer
WHERE first_name IN ('ROSA', 'ANA')
);
[ EXISTS 문을 활용한 서브 쿼리 ] - TRUE를 반환하는 경우
SELECT *
FROM customer
WHERE EXISTS(
SELECT customer_id
FROM customer
WHERE first_name IN('ROSA', 'ANA')
);
- 서브 쿼리의 결과값이 있는지 없는지를 확인해서 1행이라도 있으면 TRUE가 되어 주 쿼리 실행
[ EXISTS 문을 활용한 서브 쿼리 ] - FALSE를 반환하는 경우
SELECT *
FROM customer
WHERE EXISTS(
SELECT customer_id
FROM customer
WHERE first_name IN('KANG')
);
- 서브 쿼리의 결과값이 없어 FALSE가 되면 주 쿼리를 실행 X, 아무것도 반환하지 않음
[ NOT EXISTS를 활용한 서브 쿼리 ] - TRUE를 반환하는 경우
SELECT *
FROM customer
WHERE NOT EXISTS(
SELECT customer_id
FROM customer
WHERER first_name IN ('KANG')
);
- NOT EXISTS : EXISTS와 반대로 동작
- WHERE문에 사용된 서브 쿼리의 결과값이 없기 때문에 반대인 TRUE로 판단되어 주 쿼리 실행
[ ALL을 활용한 서브 쿼리 ]
SELECT *
FROM customer
WHERE customer_id = ALL(
SELECT customer_id
FROM customer
WHERE first_name IN ('ROSA', 'ANA')
);
- 서브 쿼리 결과값을 모두 만족하는 조건을 주 쿼리에서 찾아 결과 반환
- 여기서는 first_name이 ROSA 이면서 ANA인 데이터
- 없으므로 주 쿼리 결과에 아무것도 안 나옴
- 여기서는 first_name이 ROSA 이면서 ANA인 데이터
FROM 문에 서브 쿼리 사용하기
FROM 문에 사용한 서브 쿼리 결과는 테이블처럼 사용되어 다른 테이블과 다시 조인 가능
[ 기본 형식 ]
SELECT 열
FROM 테이블 AS a
INNER JOIN(
SELECT 열
FROM 테이블
WHERE 열 = 값
) AS b ON a.열 = b.열
WHERE 열 = 값
- FROM 문에 사용하는 서브 쿼리를 인라인뷰(inline view)라고 함
- 여기서는 INNER JOIN을 사용했지만 OUTER JOIN도 사용 가능
[ INNER JOIN 쿼리 ]
SELECT a.film_id, a.title, a.special_features, c.name
FROM film AS a
INNER JOIN film_category AS b ON a.film_id = b.film_id
INNER JOIN category AS c ON b.category_id = c.category_id
WHERE a.film_id > 10 AND a.film_id < 20;
[ FROM 문에 서브 쿼리 적용 ]
SELECT a.film_id, a.title, a.special_features, x.name
FROM film AS a
INNER JOIN(
SELECT b.film_id, c.name
FROM film_category AS b
INNER JOIN category AS c ON b.category_id = c.category_id
WHERE b.film_id > 10 AND b.film_id < 20
) AS x ON a.film_id = x.film_id;
- 위의 INNER JOIN 쿼리와 동일한 내용
- 서브 쿼리로 미리 film_category랑 category 테이블을 조건에 맞춰 조인한 테이블 x를 film테이블이랑 조인한 것
☑서브 쿼리만 드래그해서 실행해보면 FROM 문에 사용된 서브 쿼리는 격리된 것을 알 수 있음
- 서브 쿼리는 논리적으로 격리 가능 !
SELECT 문에 서브 쿼리 사용하기
SELECT 문에 쓰는 서브 쿼리는 반드시 1개의 행을 반환해야 하므로 주로 SUM 등의 집계 함수와 함께 사용
- 하지만 이러면 성능 이슈 발생할 수 있어서 집계 함수랑 쓰는 것 비추!
[ 기본 형식 ]
SELECT 열, (
SELECT <집계 함수> 열
FROM 테이블2
WHERE 테이블2.열 = 테이블1.열
) AS a
FROM 테이블1
WHERE 조건
- SELECT 문에 사용하는 서브 쿼리를 스칼라 서브 쿼리라고 함
[ INNER JOIN 쿼리 ]
SELECT a.film_id, a.title, a.special_features, c.name
FROM film AS a
INNER JOIN film_category AS b ON a.film_id = b.film_id
INNER JOIN category AS c ON b.category_id = c.category_id
WHERE a.film_id > 10 AND a.film_id < 20;
[ SELECT 문에 서브 쿼리 적용 ]
SELECT a.film_id, a.special_features, (
SELECT c.name
FROM film_category AS b
INNER JOIN category AS c ON b.category_id = c.category_id
WHERE a.film_id = b.film_id
) AS name
FROM film AS a
WHERE a.film_id > 10 AND a.film_id < 20
논리 순서
- film 테이블을 a라 별칭 후, film_id가 10 초과 ~ 20 미만인 데이터만 조회
- 이때 조회할 칼럼이 film_id, special_features, 서브쿼리 결과인 name
- 근데 이 name은 film_category랑 category테이블을 cagtegory_id를 기준으로 조인한 테이블에서
- film_id가 메인 쿼리의 film_id와 같은 카테고리 이름 (c.name)을 가져옴
'SQL' 카테고리의 다른 글
| [프로그래머스] MYSQL | 성분으로 구분한 아이스크림 총 주문량 (LV.2) (0) | 2025.03.10 |
|---|---|
| [프로그래머스] MYSQL | 카테고리 별 상품 개수 구하기 (LV.2) (0) | 2025.03.08 |
| [프로그래머스] MYSQL | 조건에 맞는 회원수 구하기 (LV.1) (0) | 2025.03.06 |
| [프로그래머스] MYSQL | 대여 기록이 존재하는 자동차 리스트 구하기 (LV.3) (0) | 2025.03.04 |