핵심 인사이트 (3줄 요약)

  1. 본질: 서브쿼리(Subquery)는 메인 쿼리(Main Query)의 괄호 ( ) 안에 숨어있는 또 다른 SELECT 구문으로, 메인 쿼리가 실행되기 위해 필요한 중간 계산 결과나 필터링 조건을 임시로 제공하는 "쿼리 속의 쿼리"다.
  2. 가치: WHERE 절에 쓰여 복잡한 다중 필터링(중첩 서브쿼리)을 돕고, FROM 절에 쓰여 가상의 임시 테이블(인라인 뷰)을 만들어내며, SELECT 절에 쓰여 단일 스칼라 값(스칼라 서브쿼리)을 추출하는 등 SQL의 표현력을 무한대로 확장시킨다.
  3. 융합: 논리적으로는 유연하지만 반복 실행(연관 서브쿼리)으로 인한 치명적 성능 저하(DB 병목)를 유발할 수 있으므로, 실무 옵티마이저(Optimizer) 튜닝 시 서브쿼리 덩어리(Unnesting)를 해체하여 일반 조인(Join)으로 풀어내는 쿼리 최적화 역량이 아키텍트의 필수 소양이다.

Ⅰ. 개요 및 필요성 (Context & Necessity)

  • 개념: 서브쿼리(Subquery)는 다른 SQL 문장(SELECT, INSERT, UPDATE, DELETE) 내부에 포함된 독립적인 SELECT 쿼리문이다. 바깥쪽에 있는 쿼리를 부모(메인 쿼리, Outer Query), 안쪽에 있는 쿼리를 자식(서브쿼리, Inner Query)이라고 부르며, 서브쿼리는 반드시 괄호 ()로 감싸져야 한다.

  • 필요성: "회사에서 전체 직원 평균 연봉보다 더 많은 연봉을 받는 사람들의 이름을 뽑아라"라는 비즈니스 요구사항을 순수하게 처리하려면 두 단계가 필요하다. 1단계: 평균 연봉을 구한다(예: 5천만원). 2단계: 연봉이 5천만원 이상인 직원을 찾는다. 과거에는 애플리케이션(Java)에서 쿼리를 두 번 날려야 했으나, 네트워크 왕복(RTT) 비용이 낭비된다. "이 두 단계를 DB 서버 안에서 한 방에 처리할 수 없을까?"라는 질문의 해답이 바로 1단계 질문을 괄호 안에 통째로 박아 넣는 서브쿼리의 탄생이다.

  • 💡 비유: 서브쿼리는 요리 프로그램에서 **"미리 만들어 둔 비법 소스"**와 같습니다. 메인 요리(메인 쿼리)를 만들기 위해 프라이팬 위에서 조리를 하다가, 중간에 별도의 작은 냄비(서브쿼리)에서 소스를 보글보글 끓여 완성한 뒤(중간 산출물), 그것을 메인 프라이팬에 싹 부어서 하나의 완벽한 요리를 한 번에 완성하는 기법입니다.

  • 등장 배경:

    1. 조인(Join)의 한계 및 복잡성: 조인 연산은 여러 테이블을 곱하기(Cartesian) 방식으로 합치다 보니 중복 데이터가 발생하고 쿼리 가독성이 떨어졌다.
    2. 관계 대수의 확장: 에드거 코드(Codd)의 관계형 모델 위에, 조건식 안에 집합 연산을 중첩할 수 있도록 SQL 스펙(ANSI SQL)이 표현력을 대폭 확장했다.
    3. 옵티마이저의 진화: 초기 서브쿼리는 무식하게 수만 번 반복 실행되어 엄청 느렸으나, 옵티마이저가 쿼리를 재작성(Query Rewrite)하여 조인으로 변환하는 지능이 생기며 대중적으로 남용(?)되기 시작했다.
┌─────────────────────────────────────────────────────────────┐
│          서브쿼리의 기본 구조와 3가지 사용 위치 (SQL 구조)          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│ SELECT  이름,                                                │
│         ( SELECT 부서명 FROM 부서 WHERE 부서.ID = 사원.부서ID )  │
│         ◀─ 1️⃣ 스칼라 서브쿼리 (Scalar Subquery) : SELECT 절에 위치│
│            단 하나의 값(1행 1열)만 무조건 뱉어내야 함!              │
│                                                             │
│ FROM    ( SELECT * FROM 사원 WHERE 급여 > 5000 ) AS 고액연봉자  │
│         ◀─ 2️⃣ 인라인 뷰 (Inline View) : FROM 절에 위치          │
│            메모리 상에 가상의 임시 테이블(View) 덩어리를 만들어 냄!   │
│                                                             │
│ WHERE   부서ID IN ( SELECT 부서ID FROM 부서 WHERE 지역='서울' )  │
│         ◀─ 3️⃣ 중첩 서브쿼리 (Nested Subquery) : WHERE 절에 위치  │
│            여러 개의 값(집합)을 뱉어내어 조건 필터링의 허들로 사용됨!  │
└─────────────────────────────────────────────────────────────┘

[다이어그램 해설] 하나의 SQL 문장에서 서브쿼리가 숨어 들어갈 수 있는 핵심 스팟 3곳을 완벽히 보여준다. SELECT 절에 들어가는 서브쿼리는 마치 함수처럼 동작하며 행(Row)마다 한 번씩 실행되어 단일 값을 뽑아낸다(스칼라). FROM 절에 들어가는 서브쿼리는 허공에 둥둥 떠 있는 가짜 테이블을 하나 임시로 빚어내어, 메인 쿼리가 그 가짜 테이블을 진짜 테이블처럼 조회하게 속이는 마법(인라인 뷰)이다. 마지막 WHERE 절에 들어가는 서브쿼리는 메인 쿼리의 데이터가 문지기를 통과할 수 있는지 기준값(예: 서울에 있는 부서 리스트)을 은밀하게 조회해서 제공하는(중첩 서브쿼리) 필터 역할을 한다.

  • 📢 섹션 요약 비유: 서브쿼리는 다목적 로봇 팔입니다. 주방(SELECT 절)에 설치하면 데코레이션(스칼라 값)을 올려주고, 도마(FROM 절)에 설치하면 새로운 그릇(임시 테이블)을 만들어주며, 검사대(WHERE 절)에 설치하면 불량품을 걸러내는 기준표(필터 조건)를 뽑아줍니다.

Ⅱ. 아키텍처 및 핵심 원리 (Deep Dive)

연관성에 따른 분류: 단일(비연관) vs 연관(Correlated) 서브쿼리

서브쿼리는 바깥쪽(메인) 쿼리와의 의존성 유무에 따라 성능이 천지 차이로 갈라진다.

분류영문 명칭원리 및 데이터 흐름실행 순서 및 성능
비연관 서브쿼리Uncorrelated Subquery서브쿼리가 메인 쿼리의 컬럼을 단 하나도 사용하지 않고 스스로 독립적으로 실행 가능함.서브쿼리가 먼저 딱 1번만 실행되어 결과를 메모리에 캐싱함. ➔ 성능 극강
연관 서브쿼리Correlated Subquery서브쿼리 내부에 메인 쿼리의 컬럼(WHERE 서브.id = 메인.id)이 끈끈하게 연결(참조)되어 있음.메인 쿼리의 행이 1만 개면, 서브쿼리도 파라미터를 넘겨받아 1만 번 반복 실행됨. ➔ 성능 지옥

비연관 서브쿼리 예시 (1번만 실행됨):

SELECT 이름 FROM 사원 
WHERE 급여 > (SELECT AVG(급여) FROM 사원); -- 독립적으로 전체 평균 5천만원을 1번만 딱 구함

연관 서브쿼리 예시 (N번 반복 실행됨):

SELECT 이름 FROM 사원 AS 외부
WHERE 급여 > (SELECT AVG(급여) FROM 사원 AS 내부 
             WHERE 내부.부서ID = 외부.부서ID); -- 직원이 100명이면 부서 평균 구하는 쿼리가 100번 돈다!

서브쿼리의 반환 형태와 사용 가능 연산자

서브쿼리가 뿜어내는 결과 덩어리의 형태(행과 열의 개수)에 따라 메인 쿼리와 결합하는 연산자가 엄격하게 제한된다.

  1. 단일 행 서브쿼리 (Single-Row): 결과가 딱 1건만 나옴. (예: MAX, MIN, AVG 등의 그룹 함수 사용 시).

    • 호환 연산자: =, <, >, <=, >=, <> (단일 값 비교)
    • 💥 에러 상황: 1건을 기대하고 =를 썼는데 2건 이상 튀어나오면 Subquery returns more than 1 row DB 에러가 뻥 터지며 서비스가 마비된다.
  2. 다중 행 서브쿼리 (Multi-Row): 결과가 2건 이상 여러 행으로 나옴. (예: 서울에 있는 부서 목록들).

    • 호환 연산자: IN, ANY, SOME, ALL, EXISTS
    • IN: 나온 결과 중 하나라도 일치하면 참.
    • EXISTS: 결과가 1건이라도 존재하기만 하면 참 (데이터 안의 실제 값은 무시하고 존재 유무만 초고속 스캔).
  3. 다중 열 서브쿼리 (Multi-Column): 컬럼 자체가 2개 이상 튀어나옴.

    • 호환: WHERE (부서ID, 직급) IN (SELECT 부서ID, 직급 FROM ...) 처럼 튜플(Tuple) 단위 비교 시 사용.
  • 📢 섹션 요약 비유: 서브쿼리가 '알약 1개(단일 행)'를 주면 메인 쿼리는 핀셋(=, >)으로 정밀하게 집어야 하고, '구슬 수십 개 묶음(다중 행)'을 던져주면 메인 쿼리는 넓은 뜰채(IN, EXISTS)를 써서 건져 올려야 에러가 나지 않습니다.

Ⅲ. 융합 비교 및 다각도 분석

비교 1: 조인 (Join) vs 서브쿼리 (Subquery) 성능 딜레마

대부분의 서브쿼리는 조인으로 100% 동일한 결과물(동치)로 변환해 작성할 수 있다. 무엇을 선택해야 할까?

항목조인 (Join) 아키텍처서브쿼리 (Subquery) 아키텍처트레이드오프 판단
가독성 (Readability)테이블이 엮이면서 SQL 코드가 방대해지고, 의도를 한눈에 파악하기 힘듦절차적(이거 구하고, 저거 걸러라)으로 읽혀서 사람 뇌 구조에 직관적이고 편함비즈니스 로직 작성의 난이도
결과 데이터 집합N x M 카테시안 곱으로 인해 결과 행(Row) 수가 뻥튀기(중복 증식) 될 위험이 큼결과가 뻥튀기되지 않음 (IN, EXISTS는 필터링 용도로만 딱 끊어짐)중복 제거(DISTINCT)의 필요성
성능 및 옵티마이저DB 엔진이 40년간 뼈를 깎아 최적화한 연산 1순위. 해시 조인 등으로 대용량 처리에 압도적 유리안티패턴(연관 스칼라 서브쿼리)으로 짜면 N+1 무한 루프 병목에 빠져 시스템 마비 가능성수백만 건 이상 대용량 DB 환경에서의 선택

개발자들은 가독성 때문에 서브쿼리(인라인 뷰)를 겹겹이 떡칠하는 것을 좋아한다. 하지만 이런 쿼리는 최신 Oracle/MySQL 옵티마이저가 내부적으로 판단하여 결국 몰래 괄호를 허물고 싹 다 일반 조인(Join)으로 강제 해체하여 변환해 버린다. 이 기술을 서브쿼리 언네스팅 (Subquery Unnesting) 이라 부른다. DB 엔진조차 서브쿼리를 '사람 보기 좋은 껍데기'일 뿐, 기계가 돌리기엔 비효율적이라고 판단하는 것이다.

과목 융합 관점

  • 운영체제 & 자료구조: 연관 서브쿼리가 N번 도는 현상은 프로그래밍의 O(N^2) 이중 for 루프(Nested Loop)와 완전히 동일한 시간 복잡도 재앙을 부른다. 10만 건 테이블에서 연관 서브쿼리를 날리면 $10만 \times 10만 = 100억$ 번의 디스크/메모리 스캔이 발생해 DB CPU를 100% 찍고 락(Lock)을 걸어 장애를 낸다.

  • 마이크로서비스 (MSA): MSA 환경에서는 DB 테이블이 여러 서버(도메인)로 찢어지기 때문에(Database per Service), 서브쿼리나 DB 내부 조인이라는 무기 자체가 아예 압수당한다. 따라서 개발자는 서버 A에서 데이터를 가져오고 메모리(Java) 단에서 for 문을 돌려 서버 B의 API를 찌르는 '애플리케이션 레벨의 더티 서브쿼리 조인' 로직을 직접 짜야 하는 고통을 겪게 된다.

  • 📢 섹션 요약 비유: 서브쿼리가 '사람이 읽기 편한 소설책'이라면, 조인은 '기계가 읽기 편한 최적화된 바코드'입니다. 뛰어난 번역가(옵티마이저)는 소설책을 받으면 몰래 속으로 바코드로 전부 번역해서 초고속으로 계산을 끝냅니다.


Ⅳ. 실무 적용 및 기술사적 판단

실무 시나리오

  1. 시나리오 — EXISTS vs IN의 극적인 성능 차이 튜닝: 배치 시스템에서 "과거에 1번 이상 구매 이력이 있는 회원 100만 명을 찾아라"라는 쿼리가 3시간째 돌고 있었다. 개발자가 WHERE 유저ID IN (SELECT 유저ID FROM 수억건의_주문내역)으로 코드를 짰기 때문이다. IN 연산자는 괄호 안의 서브쿼리를 1억 건짜리 주문 테이블 전체 스캔을 때려 임시 메모리에 수천만 건의 무거운 해시 집합을 올린 후 비교한다.

    • 판단: 시니어 DBA가 이를 WHERE EXISTS (SELECT 1 FROM 주문내역 WHERE 유저ID = 메인.유저ID) 로 단 3글자를 고쳤다. EXISTS의 마법은 "조건에 맞는 데이터가 1개라도 스캔 걸리면, 나머지 뒤의 수백만 건 주문 내역은 읽지도 않고 즉각 루프를 탈출(Short-circuit)"하는 데 있다. 3시간 걸리던 쿼리가 인덱스를 타면서 단 3초 만에 완료되는 기적이 일어났다. (물론 최신 옵티마이저는 IN을 EXISTS로 자동 변환하기도 하지만 100% 맹신할 순 없다).
  2. 시나리오 — 무분별한 스칼라 서브쿼리 떡칠로 인한 DB 사망: 프론트엔드에서 보여줄 엑셀 다운로드용 쿼리를 짜면서, 개발자가 SELECT 이름, (부서명 조회), (팀장명 조회), (과거 1년 총구매액 조회) FROM 사원 처럼 SELECT 절에 괄호(스칼라 서브쿼리)를 5개나 겹쳐 달아두었다. 사원 수가 10만 명이었고, 조회 시 순간적으로 DB가 다운되었다.

    • 판단: 스칼라 서브쿼리는 겉보기엔 우아한 함수 같지만, 실제로는 사원 1명(행)이 추출될 때마다 내부적으로 조회를 5번씩 계속해서 허공에 쏘아대는 무차별 연발 기관총이다. 사원이 10만 명이면 서브쿼리만 50만 번의 컨텍스트 스위칭 블록(Block) I/O가 터진다. 이딴 쿼리를 짜면 당장 코드를 롤백시키고, LEFT OUTER JOIN 5개로 한 번의 거대한 집합으로 묶어 한방 쿼리로 빼내는 아키텍처 대수술을 감행해야 한다.
  ┌─────────────────────────────────────────────────────────────┐
  │         실무 아키텍처: 서브쿼리 언네스팅 (Subquery Unnesting) 마법       │
  ├─────────────────────────────────────────────────────────────┤
  │                                                             │
  │ [인간 개발자가 작성한 서브쿼리 코드]                              │
  │ SELECT * FROM 고객                                           │
  │ WHERE 회원등급 = 'VIP'                                        │
  │   AND 고객ID IN (SELECT 고객ID FROM 주문 WHERE 주문액 > 100만)  │
  │ ➔ (의도: 고객 필터링 후, 주문 100만 이상인 애들 찾아내서 남겨라!)     │
  │                                                             │
  │        ======= [ 옵티마이저 (Query Transformer) 가동! ] ========│
  │        "어휴 인간아, 이거 이중 루프 돌면 너무 느려. 내가 조인으로 뜯어고친다!"│
  │                                                             │
  │ [DB 엔진 내부에서 변환되어 실제 실행되는 코드 (Join Unnesting)]    │
  │ SELECT 고객.*                                                │
  │ FROM 고객                                                    │
  │ INNER JOIN (SELECT DISTINCT 고객ID FROM 주문 WHERE 주문액 > 100만) AS 임시뷰 │
  │   ON 고객.고객ID = 임시뷰.고객ID                              │
  │ WHERE 고객.회원등급 = 'VIP'                                   │
  │                                                             │
  │ ✅ 판단: 옵티마이저는 서브쿼리 괄호를 과감히 찢어발겨 조인절로 끌어올린다. │
  │    대용량 데이터 환경에선 해시 조인(Hash Join)이 수천 배 빠르기 때문이다.   │
└─────────────────────────────────────────────────────────────┘

[다이어그램 해설] SQL은 "어떻게(How) 처리할지"를 적는 언어가 아니라 "무엇(What)을 원하는지" 결과만 던져놓는 선언적(Declarative) 언어다. 개발자가 IN 서브쿼리로 아무리 중첩을 이쁘게 짜놔도, 똑똑한 뇌(옵티마이저)는 "이거 서브쿼리로 진짜 돌리면 100만 번 반복 루프(Nested Loop) 도니까, 차라리 두 덩어리 해시 테이블을 메모리에 띄워서 한 방에 붙여버리자(Hash Join)"며 쿼리 구조 자체를 마개조(Rewrite)해 버린다. 이것이 DB의 신비이며, DBA가 EXPLAIN PLAN(실행 계획)을 반드시 까봐야 하는 이유다.

도입 체크리스트

  • 기술적: 서브쿼리가 반환하는 결과 셋에 NULL이 단 하나라도 포함되어 있는데 바깥쪽 메인 쿼리에서 NOT IN 연산자를 쓰고 있지 않은가? (NOT IN (1, 2, NULL)은 무조건 결과 전체를 FALSE(빈 화면)로 날려버리는 악명 높은 삼치 논리 버그를 낸다. 이땐 무조건 NOT EXISTS로 교체해야 한다).
  • 운영·보안적: 서브쿼리가 수십 뎁스로 중첩(인라인 뷰의 뷰의 뷰)되어, MySQL 같은 DB 엔진이 임시 테이블(Derived Table)을 메모리(RAM)에 다 담지 못하고 하드디스크 디스크(Temp 파일)로 와르르 쏟아내리며 디스크 I/O 장애를 일으키진 않는지 모니터링했는가?

안티패턴

  • SELECT * 남발 인라인 뷰: FROM (SELECT * FROM 수억건테이블) AS 임시뷰 처럼 인라인 뷰 안쪽에서 무책임하게 별(아스테리스크 *)을 치는 행위. 옵티마이저가 뷰 병합(View Merging)을 실패할 경우, DB는 수억 건 테이블의 수십 개 컬럼 데이터를 전부 다 메모리로 끌어올리는 대참사를 일으킨다. 인라인 뷰 안에서는 반드시 필요한 컬럼 단 1, 2개만 명시해야 살 수 있다.

  • 📢 섹션 요약 비유: 서브쿼리는 겹겹이 싼 마트료시카 인형입니다. 2~3겹 정도 싸는 건 예쁘고 꺼내 보기 좋지만, 10겹 20겹 무식하게 싸버리면 마지막 알맹이(결과)를 보기 위해 인형을 깠다 닫았다 하는 데 모든 체력(서버 CPU)을 다 써버리게 됩니다.


Ⅴ. 기대효과 및 결론

정량/정성 기대효과

구분무분별한 연관 서브쿼리 작성EXISTS 튜닝 및 조인 언네스팅 전환개선 효과
정량N번 반복 쿼리 (수천만 번 I/O 블록 발생)한 번의 조인 통과 (블록 스캔 극감)악성 배치 쿼리 실행 시간 시간 단위에서 초 단위로 90% 이상 단축
정량다중 컬럼 IN 메모리 풀스캔 임시 영역 폭증EXISTS 조건 Short-circuit 루프 탈출DB Temp 영역(디스크 I/O) 메모리 부하 극적으로 회피
정성요구사항 바뀔 때마다 쿼리 수정 스파게티 지옥모듈화된 논리 블록으로 가독성 및 유지보수 용이복잡한 비즈니스 룰셋 로직의 직관적 코드 맵핑 (생산성 증대)

미래 전망

  • CTE (Common Table Expression / WITH 절)의 천하통일: 최근의 쿼리 작성 트렌드는 괄호가 지저분하게 안쪽으로 파고들어 가는 서브쿼리(인라인 뷰) 패턴을 강력히 배척한다. 대신 쿼리 맨 최상단에 WITH 임시테이블 AS (SELECT ...) 형태로 미리 서브쿼리 블록을 깔끔하게 정의해두고, 메인 쿼리는 밑에서 이 블록들을 재사용하는 CTE 아키텍처가 가독성과 재사용성을 무기로 완전히 실무 대세(표준)가 되었다.
  • 옵티마이저 지능의 극한 진화: MySQL 8.0, Oracle 19c 등 최신 DB 엔진들은 개발자가 아무리 스파게티 서브쿼리를 떡칠해 놔도, 내부 코스트 기반 옵티마이저(CBO)가 알아서 "이건 스칼라 캐싱, 이건 뷰 병합, 이건 해시 조인" 하면서 인간의 실수를 99% 덮어주고 보정하는 신의 경지에 올랐다.

참고 표준

  • ANSI/ISO SQL-92: 서브쿼리, IN, EXISTS 등의 문법과 제약 조건이 정의된 국제 관계형 데이터베이스 언어 표준
  • Oracle / MySQL Optimizer 매뉴얼: Subquery Unnesting, View Merging 등 옵티마이저의 서브쿼리 변환(Rewrite) 메커니즘 가이드라인

서브쿼리는 관계형 데이터베이스가 가진 "수학적 집합론(Set Theory)"의 위대함을 가장 잘 보여주는 상징이다. 쿼리 괄호 하나를 열 때마다 가상의 우주(임시 테이블)가 하나씩 창조되고, 그 우주들이 얽히고설켜 단 하나의 진리(결과 행)를 도출해 낸다. 개발자 입장에선 사람의 사고방식대로 "위에서 아래로, 바깥에서 안으로" 생각의 흐름을 끊지 않고 로직을 코딩할 수 있게 해주는 최고의 언어적 마약이다. 단, 그 괄호가 열릴 때마다 뒷단에서 메모리와 디스크가 어떤 비명을 지르고 있는지 EXPLAIN으로 들여다볼 수 있는 개발자만이 이 마약을 해독제(최적화)로 쓸 자격이 있다.

  • 📢 섹션 요약 비유: 서브쿼리 사용은 신용카드 할부 결제와 같습니다. 긁을 당시(쿼리 작성 시)에는 내 생각대로 편하게 팍팍 써져서 쾌감이 엄청나지만, 나중에 청구서(DB 성능 장애 리포트)가 날아올 때 그 이자(N번의 반복 루프 I/O 병목)를 감당하지 못하면 시스템 전체가 파산하게 되는 무서운 마법입니다.

📌 관련 개념 맵 (Knowledge Graph)

개념 명칭관계 및 시너지 설명
Join (조인)서브쿼리의 영원한 라이벌. 가독성은 떨어지지만 대용량 해시 처리에 압도적으로 유리해, 결국 똑똑한 DB 엔진은 서브쿼리를 조인으로 강제 변환시킨다.
EXISTS vs IN 연산자서브쿼리 다중 행 처리의 양대 산맥. IN이 전체 리스트를 다 뽑아와서 비교하는 무식한 통째 수거망이라면, EXISTS는 하나라도 걸리면 낚싯대를 접는 초고속 스캔 낚시다.
인라인 뷰 (Inline View)FROM 절에 들어간 서브쿼리의 별명. 물리적 디스크 테이블이 아니라 쿼리 실행 순간에만 허공에 반짝 뜨는 신기루 같은 가짜 메모리 테이블 덩어리다.
옵티마이저 (Query Optimizer)개발자가 던진 서브쿼리의 괄호를 몰래 뜯어고치고 실행 계획을 재작성(Rewrite)하여 시스템 장애를 멱살 잡고 캐리해주는 DB 엔진 뇌 세포다.
CTE (WITH 절)괄호가 너무 많이 겹치는 인라인 뷰 서브쿼리의 가독성 지옥을 타파하기 위해 등장한 현대 SQL의 모듈화 작성 기법(표준 변수 선언)이다.

👶 어린이를 위한 3줄 비유 설명

  1. 서브쿼리는 메인 요리를 만들기 위해 주방 한쪽 구석의 작은 냄비에서 **미리 끓여두는 '비법 소스(작은 쿼리)'**예요!
  2. 5천 원이 넘는 장난감(메인 질문)을 고를 때, 내 지갑에 얼마가 있는지 1단계로 세어보는 행동(작은 질문)을 괄호 안에 쏙 숨겨두는 것과 같죠.
  3. 내 생각의 흐름대로 글을 쓰듯 쉽게 명령을 내릴 수 있어서 아주 편하지만, 이걸 10만 번 넘게 반복해서 쓰게 만들면 컴퓨터 아저씨(DB)가 지쳐서 쓰러질 수도 있으니 조심해야 한답니다!