오늘 SQL 수업은 진짜 역대급이었어. 단순히 SELECT 하는 걸 넘어서, 1억 건의 데이터 앞에서도 당당할 수 있는 성능 중심의 쿼리 짜는 법을 배웠거든. 재밌기도,, 어렵기도,, 아직 많이 헷갈리지만 잊어버리기 전에 반말로 빡세게 정리한다!
1. 기초 다지기: Group By & Join
- Group By: 부서별 평균 급여 같은 집계 데이터를 만들 때 필수야. 집계함수(AVG, SUM 등)랑 찰떡궁합이지.
- Join: 흩어진 테이블을 하나로 합쳐.
- Self Join: 특히 중요! 네이버 면접에도 나왔던 전날보다 온도 높은 날 찾기처럼 같은 테이블 내에서 시간이나 선후 관계를 비교할 때 최고의 무기야.
- + 추가 join 상세한 설명은 이 전에 올려놓은 join설명을 보면 도움이 될거야! https://superpark.tistory.com/9
2. 서브쿼리(Subquery)의 3가지 얼굴
서브쿼리는 위치에 따라 이름도, 성격도 달라.
① 스칼라 서브쿼리 (Scalar Subquery)
- 위치: SELECT 절
- 특징: 한 칸에 들어갈 단일 값(1행 1열)만 뱉어야 해. 엑셀의 VLOOKUP 같은 느낌!
② 인라인 뷰 (Inline View) - 중요!
- 위치: FROM 절
- 특징: 서브쿼리 결과를 임시 테이블로 취급해.
- 꿀팁: 여기서 별칭(Alias) 안 붙이면 무조건 에러 나. 컴퓨터가 이 임시 덩어리를 뭐라고 불러야 할지 모르거든. AS는 생략 가능하지만, 이름표 자체는 절대 빼먹지 마!
③ 상관 서브쿼리 (Correlated Subquery)
- 특징: 메인 쿼리 한 줄 읽을 때마다 서브쿼리가 계속 실행돼.
- 용도: 내 부서의 평균처럼 기준이 행마다 바뀔 때 사용해.
3. 성능 대결: EXISTS vs IN
데이터가 1억 건쯤 되면 여기서 승부가 갈려.
- EXISTS: 있나 없나만 봐. 하나라도 찾으면 그 즉시 멈추고 True를 반환하니까 대용량 데이터에서 엄청 빨라.
- IN: 리스트를 일단 다 뽑아서 비교해. 서브쿼리 쪽 데이터가 많으면 EXISTS보다 훨씬 무거워져.
데이터가 100만 건이 넘어가면 여기서 실력이 갈려.
| 연산자 | 특징 | 비유 |
| IN | 서브쿼리 결과를 메모리에 다 올리고 비교 | 전단지 명단을 다 들고 다니며 대조 |
| EXISTS | 조건에 맞는 거 하나라도 찾으면 즉시 중단 | "있나?" 확인하고 발견하면 바로 복귀 (가장 빠름) |
| ALL | 모든 값보다 만족해야 함 (> ALL = 최대값보다 큼) | 반에서 제일 큰 애보다 커야 함 |
| ANY | 하나만 만족해도 됨 (> ANY = 최소값보다 큼) | 반에서 제일 작은 애보다만 크면 됨 |
4. 인덱스(Index)와 시간 복잡도의 비밀
왜 인덱스, 인덱스 하는 걸까?
- 인덱스: 책 뒤의 찾아보기야. 이게 없으면 처음부터 다 뒤지는 Full Scan(시간 복잡도 N)을 해야 해.
- 자동 인덱스: PK(기본키)나 FK(외래키)는 DB가 알아서 인덱스를 깔아줘. 자주 쓰는 길이니까 미리 닦아두는 거지.
- log N의 마법: 인덱스를 타면 데이터가 100만 배 늘어나도 찾는 속도는 아주 조금만 느려져. 강사님이 말한 N log N은 메인 테이블은 다 훑어도(N), 연결된 서브쿼리는 인덱스로 빠르게 찾는다(log N)는 뜻이야!
- Full Table Scan (O(n)): 인덱스 없이 처음부터 끝까지 다 뒤지는 것. 데이터가 늘어날수록 속도가 정비례해서 느려짐.
- Index Range Scan (O(log n)): 인덱스(B-Tree)를 타고 필요한 부분만 골라 읽는 것. 데이터가 1,000배 늘어나도 읽는 횟수는 몇 번 안 늘어남.
- SARGable Query (인덱스를 탈 수 있는 쿼리):
- X WHERE DATE_FORMAT(reg_date, '%Y') = '2025' (컬럼 가공 시 인덱스 파괴)
- O WHERE reg_date >= '2025-01-01' AND reg_date < '2026-01-01' (원본 유지)
5. 집합 연산 (Set Operations)
- UNION: 중복 제거하고 합치기.
- INTERSECT(교집합) & MINUS(차집합): MySQL 구버전은 이걸 직접 지원 안 해.
- 해결책: 당황하지 말고 우리가 배운 INNER JOIN이나 NOT EXISTS로 똑같이 구현하면 돼. 결국 서브쿼리가 본체라는 소리!
@ 오늘의 최종 결론
쿼리를 잘 짠다는 건 단순히 결과가 나오는 게 아니야. 어디에 인덱스가 걸려 있지?, 이건 EXISTS가 빠를까, JOIN이 빠를까?를 고민하며 컴퓨터의 노가다를 줄여주는 전략가가 되는 거야! 댕 어렵다.. 난 다시 처음부터 공부하러 가볼게 !
