예를 들어, 너가 대학 데이터베이스에서 일하고 있는데, 특정 과목을 듣는 학생들을 찾아야 해. 예를 들어 "프로그래밍", "수학", "물리"를 듣는 학생들 말이야. 물론, 아래처럼 조건문을 길게 쓸 수도 있지:
SELECT *
FROM students
WHERE course = '프로그래밍'
OR course = '수학'
OR course = '물리';
근데 솔직히 이런 식으로 쓰는 거 귀찮고, 별로 멋지지도 않아. 다행히 IN 연산자가 있어서, 똑같은 쿼리를 훨씬 간단하게 쓸 수 있어. 시간도 아끼고:
SELECT *
FROM students
WHERE course IN ('프로그래밍', '수학', '물리');
진짜 마법 같지? OR 여러 개 쓰는 대신, 그냥 SQL한테 이 리스트 안에 있는 값만 찾아달라고 하면 돼. 만약 값이 아닌 것만 찾고 싶으면 NOT IN을 쓰면 돼 — 이 리스트에 없는 것만 찾아줘 이런 느낌이야.
IN 연산자 문법
IN 연산자 기본 문법은 이래:
SELECT 컬럼들
FROM 테이블
WHERE 컬럼 IN (값1, 값2, 값3, ...);
이제 예제 몇 개 볼까.
예제 1: 여러 과목을 듣는 학생들
예를 들어, students 테이블이 있다고 해보자:
| id | name | course |
|---|---|---|
| 1 | Anna | 프로그래밍 |
| 2 | Mello | 물리 |
| 3 | Kate | 수학 |
| 4 | Dan | 화학 |
| 5 | Olly | 생물학 |
"프로그래밍", "수학", "물리" 듣는 학생들만 찾고 싶으면 IN을 이렇게 써:
SELECT name, course
FROM students
WHERE course IN ('프로그래밍', '수학', '물리');
결과:
| name | course |
|---|---|
| Anna | 프로그래밍 |
| Mello | 물리 |
| Kate | 수학 |
보다시피 IN 연산자 덕분에 훨씬 간단해졌지. OR 여러 개 안 써도 되고, 그냥 관심 있는 값들만 리스트로 넣으면 돼.
예제 2: 특정 과목을 안 듣는 학생들
이번엔 "프로그래밍", "수학", "물리" 안 듣는 학생들만 찾고 싶으면 NOT IN을 써:
SELECT name, course
FROM students
WHERE course NOT IN ('프로그래밍', '수학', '물리');
결과:
| name | course |
|---|---|
| Dan | 화학 |
| Olly | 생물학 |
이렇게 NOT IN은 course 컬럼 값이 리스트에 없는 모든 행을 반환해.
IN이랑 NOT IN을 서브쿼리랑 같이 쓰기
IN이랑 NOT IN은 두 테이블에서 데이터 비교할 때 진짜 유용해. 예를 들어, 두 개 테이블이 있다고 해보자:
students 테이블:
| id | name | course_id |
|---|---|---|
| 1 | Anna | 101 |
| 2 | Mello | 102 |
| 3 | Kate | 103 |
| 4 | Dan | 104 |
courses 테이블:
| id | name |
|---|---|
| 101 | 프로그래밍 |
| 102 | 물리 |
| 103 | 수학 |
| 105 | 화학 |
이제 courses 테이블에 실제로 존재하는 과목에 등록된 학생만 찾고 싶으면, IN이랑 서브쿼리를 이렇게 써:
SELECT name
FROM students
WHERE course_id IN (
SELECT id
FROM courses
);
이 쿼리는 이렇게 동작해: 서브쿼리 SELECT id FROM courses가 모든 과목 id를 반환하고, IN이 course_id가 그 리스트에 있는지 체크해.
결과:
| name |
|---|
| Anna |
| Mello |
| Kate |
Dan이 빠진 이유? Dan의 course_id (104)는 courses 테이블에 없기 때문이야.
NULL 다룰 때 주의할 점
IN 연산자는 중요한 특징이 하나 있어: 값 리스트에 NULL이 있으면 쿼리 결과에 영향을 줄 수 있어. 예제 보자.
grades 테이블:
| student_id | course_id | grade |
|---|---|---|
| 1 | 101 | A |
| 2 | 102 | NULL |
| 3 | 103 | B |
('A', 'B', 'C') 중 하나인 점수를 가진 학생을 찾는 쿼리는 이렇게 쓸 수 있어:
SELECT student_id
FROM grades
WHERE grade IN ('A', 'B', 'C');
결과:
| student_id |
|---|
| 1 |
| 3 |
grade 컬럼이 NULL인 행은 무시돼. 왜냐면 NULL은 어떤 리스트에도 포함된 걸로 안 치거든.
이번엔 NOT IN을 써보자. 예를 들어:
SELECT student_id
FROM grades
WHERE grade NOT IN ('A', 'B', 'C');
student_id = 2인 행이 나올 거라 기대할 수도 있는데, 결과는 아무것도 없어! 왜냐면 NULL이 리스트의 각 값이랑 비교될 때마다 결과가 항상 미정(UNKNOWN)이기 때문이야. 이거 헷갈릴 수 있으니까, NOT IN 쓸 때 NULL 컬럼이 있으면 꼭 신경 써야 해. 이런 경우엔 NULL 체크를 명시적으로 추가하는 게 좋아:
SELECT student_id
FROM grades
WHERE grade NOT IN ('A', 'B', 'C')
OR grade IS NULL;
결과:
| student_id |
|---|
| 2 |
IN이랑 NOT IN 쓸 때 팁
IN을 써서 SQL 코드 가독성 높이기
컬럼 값이 특정 리스트에 있는지 확인할 때는 OR 여러 개 쓰는 것보다 IN을 쓰는 게 훨씬 좋아.
NOT IN이랑 NULL 조심하기
데이터에 NULL 컬럼이 있으면 예상치 못한 결과가 나올 수 있어. NOT IN 쓸 땐 NULL을 명시적으로 처리해줘.
서브쿼리 성능 높이려면 인덱스 쓰기
IN을 서브쿼리랑 같이 쓸 때, 서브쿼리 컬럼에 인덱스가 있으면 성능 저하를 막을 수 있어.
실전 예제
이번엔 인터넷 쇼핑몰 시스템에서 일한다고 해보자. orders랑 users 테이블이 있어. 한 번도 주문 안 한 유저만 찾고 싶어.
users 테이블:
| id | name |
|---|---|
| 1 | Anna |
| 2 | Mello |
| 3 | Kate |
| 4 | Dan |
orders 테이블:
| id | user_id | total |
|---|---|---|
| 1 | 1 | 500 |
| 2 | 3 | 300 |
NOT IN으로 문제를 풀어보자:
SELECT name
FROM users
WHERE id NOT IN (
SELECT user_id
FROM orders
);
결과:
| name |
|---|
| Mello |
| Dan |
이 쿼리는 이렇게 동작해: 먼저 서브쿼리 SELECT user_id FROM orders가 주문한 유저 id(1, 3)를 반환하고, NOT IN이 그 유저들을 빼고 주문 안 한 유저(Mello, Dan)만 남겨줘.
GO TO FULL VERSION