본문 바로가기

비개발자의 개발 일지

현장 팀장에서 수출 ERP까지 — AI와 함께 시스템을 만든 기록

시리즈 보기
php

MySQL 서브쿼리 · ANY · ALL · IN 완전 정리

by 왕진 2026. 7. 14.
반응형

 

 

Web Development · MySQL

MySQL 서브쿼리 · ANY · ALL · IN —
중첩 쿼리 완전 정리

WHERE 절 서브쿼리 · ANY / ALL / IN 비교 · 실행 순서 이해
시작 오늘 분석할 코드
-- 김경호보다 키가 큰 회원 SELECT name, height FROM usertbl WHERE height > (SELECT height FROM usertbl WHERE name = '김경호'); -- 경남 출신 중 누구보다도 크거나 같은 키의 회원 (ANY = OR) SELECT name, height FROM usertbl WHERE height >= ANY (SELECT height FROM usertbl WHERE addr = '경남'); -- 경남 출신 모두보다 크거나 같은 키의 회원 (ALL = AND) SELECT name, height FROM usertbl WHERE height >= ALL (SELECT height FROM usertbl WHERE addr = '경남'); -- = ANY는 IN과 동일 SELECT name, height FROM usertbl WHERE height = ANY (SELECT height FROM usertbl WHERE addr = '경남'); SELECT name, height FROM usertbl WHERE height IN (SELECT height FROM usertbl WHERE addr = '경남');
서브쿼리(subquery)는 쿼리 안에 또 다른 쿼리가 들어가는 형태다. 안쪽 쿼리(서브쿼리)가 먼저 실행되고, 그 결과를 바깥 쿼리가 조건으로 사용한다.

1 서브쿼리 개념 — 쿼리 안의 쿼리

복잡한 조건을 두 번의 쿼리 대신 하나로 표현할 때 서브쿼리를 쓴다. 예를 들어 "김경호보다 키가 큰 사람"을 찾으려면 먼저 김경호의 키를 알아야 한다.

-- 방법 1: 두 개의 쿼리 (번거로움) SELECT height FROM usertbl WHERE name = '김경호'; -- 결과: 177 SELECT name, height FROM usertbl WHERE height > 177; -- 방법 2: 서브쿼리 (한 번에) SELECT name, height FROM usertbl WHERE height > (SELECT height FROM usertbl WHERE name = '김경호'); -- 안쪽 SELECT가 먼저 실행 → height > 177로 치환
서브쿼리 위치 역할 예시
WHERE 절 조건값으로 사용 (가장 일반적) WHERE height > (SELECT ...)
SELECT 절 계산값을 컬럼으로 (스칼라 서브쿼리) SELECT (SELECT COUNT(*) FROM ...)
FROM 절 임시 테이블로 (인라인 뷰) FROM (SELECT ... ) AS t

2 단일값 vs 다중값 서브쿼리

서브쿼리가 돌려주는 값이 1개냐 여러 개냐에 따라 쓸 수 있는 연산자가 달라진다.

-- 단일값 서브쿼리: 결과가 정확히 1개 행, 1개 열 SELECT height FROM usertbl WHERE name = '김경호' -- → 177 (1개) -- = / != / > / < / >= / <= 모두 사용 가능 WHERE height > (SELECT height FROM usertbl WHERE name = '김경호') -- 다중값 서브쿼리: 결과가 여러 행 SELECT height FROM usertbl WHERE addr = '경남' -- → 170, 173, 176 (여러 개) -- = 단독 사용 불가 (오류), IN / ANY / ALL 사용 WHERE height IN (SELECT height FROM usertbl WHERE addr = '경남')
단일값 서브쿼리 자리에 여러 행이 돌아오면 오류가 난다 — "Subquery returns more than 1 row". 다중행이 예상되면 IN / ANY / ALL을 쓴다.

3 ANY vs ALL vs IN 비교

서브쿼리가 여러 값을 반환할 때 그 값들과 어떻게 비교할지를 결정하는 키워드다.

ANY (= SOME)
서브쿼리 결과 중 하나라도 조건을 만족하면 TRUE — OR처럼 동작
ALL
서브쿼리 결과 모두를 만족해야 TRUE — AND처럼 동작
IN
= ANY와 동일. 서브쿼리 결과 목록 중 하나와 같으면 TRUE
NOT IN
목록에 없으면 TRUE. 단, 목록에 NULL이 있으면 예외 주의
-- 경남 출신 키 목록: 170, 173, 176 -- >= ANY: 170 이상 OR 173 이상 OR 176 이상 → 170 이상인 모든 회원 WHERE height >= ANY (SELECT height FROM usertbl WHERE addr = '경남') -- 결과: height >= 170 (최솟값 이상이면 ANY 조건 성립) -- >= ALL: 170 이상 AND 173 이상 AND 176 이상 → 176 이상인 회원 WHERE height >= ALL (SELECT height FROM usertbl WHERE addr = '경남') -- 결과: height >= 176 (최댓값 이상이어야 ALL 조건 성립) -- = ANY: 170 또는 173 또는 176인 회원 (= IN) WHERE height = ANY (SELECT height FROM usertbl WHERE addr = '경남') WHERE height IN (SELECT height FROM usertbl WHERE addr = '경남') -- = ANY와 IN은 완전히 동일
비교 연산 ANY ALL 결과 (경남 키: 170, 173, 176)
>= ANY 최솟값(170) 이상이면 OK height >= 170인 회원
>= ALL 최댓값(176) 이상이어야 OK height >= 176인 회원
= ANY = IN 목록 중 하나와 같으면 OK height가 170, 173, 176인 회원
<= ANY 최댓값(176) 이하면 OK height <= 176인 회원
<= ALL 최솟값(170) 이하여야 OK height <= 170인 회원

 

반응형

 

4 서브쿼리 실행 순서

서브쿼리가 포함된 쿼리는 항상 안쪽(서브쿼리) 먼저, 바깥쪽 나중 순서로 실행된다.

-- 실행 순서 예시 SELECT name, height FROM usertbl WHERE height > (SELECT height FROM usertbl WHERE name = '김경호'); -- ① 안쪽 쿼리 실행 -- SELECT height FROM usertbl WHERE name = '김경호' -- → 결과: 177 -- ② 바깥쪽 쿼리 실행 -- SELECT name, height FROM usertbl WHERE height > 177 -- → 177보다 큰 height를 가진 회원 출력
서브쿼리를 이해하는 핵심: 안쪽 괄호를 먼저 실행해서 값으로 바꾼 뒤 바깥 쿼리에 대입한다. 마치 수학의 괄호 먼저 계산과 같다.

5 FROM 절 서브쿼리 (인라인 뷰)

서브쿼리를 FROM 절에 넣어 임시 테이블처럼 사용할 수 있다. 인라인 뷰(inline view)라고도 부른다.

-- 총 구매액 상위 3명의 이름도 같이 보고 싶을 때 -- buytbl에는 이름이 없고 usertbl과 JOIN이 필요하지만, 서브쿼리로 단계적으로도 가능 -- FROM 절 서브쿼리: 회원별 총 구매액을 먼저 집계 SELECT t.userID, t.총구매액 FROM ( SELECT userID, SUM(price*amount) AS 총구매액 FROM buytbl GROUP BY userID ) AS t WHERE t.총구매액 >= 1000 ORDER BY t.총구매액 DESC; -- 서브쿼리 결과에 별칭 t를 붙여 외부에서 t.컬럼으로 참조
FROM 절 서브쿼리에는 반드시 AS 별칭을 붙여야 한다. 붙이지 않으면 "Every derived table must have its own alias" 오류가 난다.

6 자주 하는 실수
실수 증상 해결
단일값 자리에 다중행 반환 Subquery returns more than 1 row IN / ANY / ALL 사용 또는 LIMIT 1 추가
= ANY와 IN 혼동 의미적으로 동일해서 문제 없지만 혼란 = ANY = IN, 가독성 좋은 IN을 주로 사용
FROM 서브쿼리 별칭 생략 Every derived table must have alias FROM (...) AS t 형태로 반드시 별칭 붙이기
NOT IN + NULL 포함 목록 결과 없음 (0건) NOT IN 목록에 NULL 없도록 WHERE col IS NOT NULL 추가
서브쿼리 결과 0행 바깥 쿼리 결과도 없음 서브쿼리 단독 실행으로 결과 먼저 확인

핵심 한 줄 요약

서브쿼리쿼리 안의 쿼리 — 안쪽이 먼저 실행되어 값으로 치환됨
단일값 서브쿼리= / != / > / < 등 비교 연산자 사용 가능
다중행 서브쿼리IN / ANY / ALL 필수 — = 단독 사용 시 오류
>= ANY목록 중 최솟값 이상 — OR 조건처럼 동작
>= ALL목록 중 최댓값 이상 — AND 조건처럼 동작
= ANY = IN동일한 의미 — IN이 더 가독성 좋아 주로 사용
NOT IN목록에 없는 행 — NULL 포함 시 결과 없음 주의
FROM 절 서브쿼리인라인 뷰 — 임시 테이블처럼 사용, AS 별칭 필수
실행 순서안쪽 서브쿼리 먼저 → 결과를 바깥 WHERE에 대입
서브쿼리 vs JOIN단순 조건은 서브쿼리, 두 테이블 동시 출력은 JOIN
디버깅 팁서브쿼리만 단독 실행해서 반환값 먼저 확인
EXISTS서브쿼리 결과가 1건이라도 있으면 TRUE — 존재 여부 체크
서브쿼리 위치 3곳WHERE 절 / SELECT 절 (스칼라) / FROM 절 (인라인 뷰)
LIMIT 1 활용다중행 서브쿼리를 단일값으로 강제 변환 (성능 주의)
서브쿼리 vs IN(목록)IN ('서울', '경남')은 목록 직접 지정, IN (SELECT...)는 동적 결과 사용
상관 서브쿼리바깥 쿼리의 컬럼을 서브쿼리가 참조 — 행마다 서브쿼리 실행
중첩 서브쿼리서브쿼리 안에 또 서브쿼리 — 논리적으로 가능, 복잡해지면 JOIN 고려
성능 주의서브쿼리는 매 행마다 실행될 수 있어 대용량 테이블에서 느릴 수 있음
SELECT 절 서브쿼리SELECT (SELECT COUNT(*) FROM buytbl) AS 총구매건수 — 스칼라 서브쿼리
서브쿼리 재작성복잡한 서브쿼리는 JOIN으로 바꾸면 읽기 쉽고 빠른 경우 많음
ANY = SOMEANY와 SOME은 동일한 키워드 — MySQL에서 둘 다 사용 가능
중첩 깊이서브쿼리는 여러 단계 중첩 가능 — 하지만 2단계 이상은 JOIN 추천
동적 IN 목록IN (SELECT userID FROM usertbl WHERE addr = '서울') — 값이 고정 안 된 경우
서브쿼리 비교 정리> ANY=최솟값 초과 / > ALL=최댓값 초과 / IN=목록 일치 / NOT IN=목록 제외
서브쿼리 사용 시기다른 쿼리 결과를 조건으로 쓸 때, 값을 미리 모를 때
WHERE IN 최적화IN 목록이 많으면 성능 저하 → EXISTS나 JOIN으로 대체 고려
서브쿼리 NULL 주의서브쿼리 결과에 NULL 있으면 NOT IN의 전체 결과가 0건이 됨
= (서브쿼리) 패턴WHERE height = (SELECT MAX(height) FROM usertbl) — 최댓값 가진 회원
서브쿼리 결과 확인서브쿼리를 단독 SELECT로 실행 → 반환값/행 수 먼저 확인
HAVING의 서브쿼리HAVING SUM(amount) > (SELECT AVG(amount) FROM buytbl) — 평균 초과 그룹
UPDATE/DELETE 서브쿼리UPDATE tbl SET ... WHERE id IN (SELECT id FROM ...) — 조건부 수정
서브쿼리 한계MySQL에서 UPDATE/DELETE 대상 테이블을 서브쿼리에서 직접 참조 불가
ANY / ALL 실무실무에선 IN이 주류, ANY/ALL은 범위 비교 시 MIN/MAX 대신 간결하게
서브쿼리 단독 실행 팁문제 생기면 () 안 SELECT만 따로 실행해 반환값 확인
= vs IN서브쿼리 결과 1행 보장 시 =, 여러 행 가능 시 IN 사용
중첩 서브쿼리 예시WHERE amount > (SELECT AVG(amount) FROM buytbl WHERE price > 50)
서브쿼리 적용 범위SELECT/FROM/WHERE/HAVING/UPDATE/DELETE 절 모두에서 사용 가능
NOT EXISTSWHERE NOT EXISTS (서브쿼리) = 해당 행이 없는 경우 TRUE
IN vs JOIN 성능소규모 데이터는 IN, 대규모는 JOIN이 일반적으로 빠름
다중 컬럼 비교WHERE (a, b) IN (SELECT x, y FROM ...) — 두 컬럼 조합 일치 확인
ALL(빈 목록)ALL (빈 서브쿼리) = TRUE — 비교 대상 없으면 항상 TRUE
ANY(빈 목록)ANY (빈 서브쿼리) = FALSE — 비교 대상 없으면 항상 FALSE
IN 목록 생성IN ('KBS','BBK','EJW') = 정적 목록, IN (SELECT...) = 동적 목록
서브쿼리 패턴 정리단순비교→= / 범위→ANY/ALL / 목록→IN / 존재여부→EXISTS
FROM 서브쿼리 별칭FROM (SELECT ...) AS t — 반드시 AS 별칭 필요 (표준 SQL)
서브쿼리 실무 예시WHERE salary > (SELECT AVG(salary) FROM emp) — 평균 이상 직원

Tags

#MySQL #서브쿼리 #ANY #ALL #IN #EXISTS #중첩쿼리 #SQL #데이터베이스 #웹개발 #티스토리
▼ 티스토리 태그 입력란 복사용
MySQL, 서브쿼리, ANY, ALL, IN, EXISTS, 중첩쿼리, SQL, 데이터베이스, 웹개발, 티스토리
반응형

댓글