반응형
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, 데이터베이스, 웹개발, 티스토리
반응형
'php' 카테고리의 다른 글
| MySQL MIN · MAX — 최솟값·최댓값 완전 정리 (0) | 2026.07.14 |
|---|---|
| MySQL 테이블 복사 · CREATE TABLE SELECT 완전 정리 (0) | 2026.07.14 |
| MySQL GROUP BY · 집계함수 · AS 완전 정리 (0) | 2026.07.14 |
| MySQL ORDER BY · DISTINCT · LIMIT 완전 정리 (0) | 2026.07.14 |
| MySQL INSERT INTO — 데이터 삽입 완전 정리 (0) | 2026.07.14 |
왕진 블로그

댓글