본문 바로가기
SQL

[Do it! SQL 입문] 서브쿼리

by kumanchii 2025. 5. 5.

쿼리 안에 또 다른 쿼리, 서브쿼리

 

 

서브쿼리의 특징

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인 데이터
      • 없으므로 주 쿼리 결과에 아무것도 안 나옴

 

 

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

 

 

논리 순서

  1. film 테이블을 a라 별칭 후, film_id가 10 초과 ~ 20 미만인 데이터만 조회
  2. 이때 조회할 칼럼이 film_id, special_features, 서브쿼리 결과인 name
  3. 근데 이 name은 film_category랑 category테이블을 cagtegory_id를 기준으로 조인한 테이블에서
  4. film_id가 메인 쿼리의 film_id와 같은 카테고리 이름 (c.name)을 가져옴