서브쿼리 쓸 때 흔히 하는 실수들
서브쿼리 다루는 건 마치 벗고 하는 체스 같은 느낌이야: 처음엔 쉬워 보여도, 한 수 잘못 두면 바로 실수 터진다. 그럼 실수는 왜 생길까? 문법을 잘못 이해하거나, SQL 논리 특성을 무시하거나, 그냥 대충 하다가 그런 거지. 이번 강의에서는 자주 나오는 실수들이랑, 그걸 어떻게 피할 수 있는지 얘기해볼게.
문법 실수
서브쿼리는 문법에 진짜 신경 써야 해. 콤마, 괄호, alias 하나만 빠져도 쿼리 전체가 망가질 수 있어. 몇 가지 흔한 문제를 같이 보자.
괄호 빠뜨리기
괄호는 서브쿼리 쓸 때 핵심이야. 서브쿼리는 항상 소괄호로 감싸야 하고, 한 쌍이라도 빠지면 문법 에러 난다.
실수 예시:
SELECT student_name
FROM students
WHERE student_id IN SELECT student_id FROM enrollments);
에러:
ERROR: syntax error at or near "SELECT"
수정:
SELECT student_name
FROM students
WHERE student_id IN (SELECT student_id FROM enrollments);
코멘트: IN 안에 들어가는 쿼리는 항상 괄호로 감싸야 SQL이 "아, 이거 서브쿼리구나" 하고 알아듣는다.
alias 안 줌
FROM에서 서브쿼리 쓸 때는 alias 꼭 줘야 해. 안 그러면 PostgreSQL이 헷갈려서 에러 난다.
실수 예시:
SELECT student_name, avg_score
FROM (SELECT student_id, AVG(score) AS avg_score FROM grades GROUP BY student_id)
WHERE avg_score > 80;
에러:
ERROR: subquery in FROM must have an alias
수정:
SELECT student_name, avg_score
FROM (SELECT student_id, AVG(score) AS avg_score FROM grades GROUP BY student_id) AS subquery
WHERE avg_score > 80;
코멘트: PostgreSQL에서는 임시 테이블(즉, FROM에 들어가는 서브쿼리 결과)에 이름(alias)이 꼭 있어야 해.
성능 문제
서브쿼리, 특히 최적화 안 된 거 쓰면 DB가 느려터진다. 성능이 안 나오는 이유는 쓸데없는 계산이 많거나, 인덱스가 없어서 그래.
쓸데없는 계산
SELECT 안에 서브쿼리 넣으면 결과의 각 행마다 그 쿼리를 다시 돌리게 돼서 시간 엄청 잡아먹는다.
예시:
SELECT student_name,
(SELECT COUNT(*) FROM enrollments WHERE enrollments.student_id = students.student_id) AS course_count
FROM students;
students 테이블에 만약 수만 개 행이 있으면, 이 서브쿼리가 매번 새로 실행된다.
최적화:
WITH course_counts AS (
SELECT student_id, COUNT(*) AS course_count
FROM enrollments
GROUP BY student_id
)
SELECT s.student_name, c.course_count
FROM students s
LEFT JOIN course_counts c ON s.student_id = c.student_id;
CTE(Common Table Expression)나 join을 쓰면 매 행마다 다시 계산 안 해도 돼. 이건 좀 더 나중에 자세히 다룰 거야 :P
인덱스 없음
WHERE에 복잡한 서브쿼리 쓸 때는, 필요한 컬럼에 인덱스가 있는지 꼭 확인해.
예시:
SELECT student_name
FROM students
WHERE student_id IN (SELECT student_id FROM enrollments WHERE course_id = 10);
enrollments 테이블의 student_id 컬럼에 인덱스가 없으면, 서브쿼리가 테이블 전체를 다 뒤진다.
최적화: 인덱스 만들어주기:
CREATE INDEX idx_enrollments_course_id ON enrollments (course_id);
인덱스 얘기 맨날 나오니까 궁금할 텐데, 조금만 더 기다려봐. 인덱스는 쿼리 속도 올리는 데 중요한데, 쿼리 자체를 바꾸는 건 아니야. 나쁜 쿼리를 좋은 쿼리로 만들어주진 않고, production에서 진짜 큰 테이블(수백만 행) 다룰 때 효과가 커.
논리 실수
서브쿼리에서 논리 실수도 문법 실수만큼 자주 나와. NULL 처리, 필터 조건, 집계 함수 같은 거 잘못 쓰면 결과가 완전 엉뚱하게 나올 수 있어.
NULL 잘못 다루기
NULL은 초보자한테 진짜 함정이야. IN이나 NOT IN에 서브쿼리 쓸 때 NULL이 있으면 결과가 달라질 수 있어.
실수 예시:
SELECT student_name
FROM students
WHERE student_id NOT IN (SELECT student_id FROM enrollments);
enrollments 테이블에 student_id = NULL인 행이 있으면, 이 쿼리는 아무것도 안 돌려줘. NOT IN 조건이 NULL IS NOT IN처럼 동작해서 그래.
수정:
SELECT student_name
FROM students
WHERE student_id NOT IN (SELECT student_id FROM enrollments WHERE student_id IS NOT NULL);
NOT IN 쓸 때는 항상 NULL을 필터링해줘야 해.
필터 조건 실수
서브쿼리로 복잡한 필터링 할 때, 조건 잘못 쓰면 결과가 완전 다르게 나올 수 있어.
실수 예시:
SELECT student_name
FROM students
WHERE (SELECT AVG(score) FROM grades WHERE grades.student_id = students.student_id) > 80;
만약 어떤 학생이 점수가 하나도 없으면, 서브쿼리가 NULL을 돌려주고, 그 학생은 결과에 안 나와.
수정:
SELECT student_name
FROM students
WHERE COALESCE((SELECT AVG(score) FROM grades WHERE grades.student_id = students.student_id), 0) > 80;
COALESCE를 써서 NULL을 기본값으로 바꿔줘.
실수 안 하려면 이렇게 해봐
서브쿼리 쓸 때 흔한 실수 피하려면, 아래 규칙들 꼭 기억해:
괄호랑 alias 제대로 쓰기. 뭔가 안 되면, 괄호 다 닫았는지, alias 빠진 거 없는지부터 확인해봐.
쿼리 최적화. 서브쿼리 남발하지 말고, JOIN, WITH, 인덱스 같은 거 쓸 수 있으면 써봐.
NULL 신경쓰기. 서브쿼리에 NULL 나올 수 있으면 IS NOT NULL, COALESCE 같은 거 꼭 써줘.
테스트하기. 서브쿼리 하나씩 따로 돌려서 결과 제대로 나오는지 꼭 확인해.
가독성 챙기기. 들여쓰기랑 alias 잘 써서 코드 읽기 쉽게 해. 한 달 뒤에 보면 네가 쓴 쿼리 네가 못 알아볼 수도 있어.
GO TO FULL VERSION