CodeGym /행동 /SQL SELF /SELECT에서 서브쿼리 사용하기

SELECT에서 서브쿼리 사용하기

SQL SELF
레벨 14 , 레슨 0
사용 가능

한 번 더 SELECT에서 서브쿼리 쓰는 얘기로 돌아가 볼게. 특히 내부 쿼리가 외부 쿼리의 데이터를 참조할 수 있다는 점에 집중해서! 뭔가 쉬워 보이지만, 또 막상 해보면 헷갈릴 수 있거든. 다시 한 번 이 주제 깊게 파고들어보자...

SELECT에서 서브쿼리를 쓰면 다른 레코드나 테이블에 따라 달라지는 계산된 값이나 데이터를 추가 컬럼으로 넣을 수 있어. 예를 들어, 학생 리스트를 뽑으면서 각자의 평균 점수, 수강 중인 과목 개수, 혹은 그룹 내 최고 점수 같은 걸 바로 보여줄 수 있지. 이런 식으로 데이터를 "실시간"으로 분석하고, 미리 가공하지 않아도 집계 컬럼을 만들 수 있어서 꽤 유용해.

SELECT에서 서브쿼리 기본

예제 보기 전에, 기본 문법부터 짚고 가자. SELECT에서 서브쿼리는 이렇게 생겼어:

SELECT column1,
       column2,
       (SELECT 집계_또는_조건 FROM 다른_테이블 WHERE 조건) AS 새_컬럼_이름
FROM 메인_테이블;

여기서 서브쿼리는 값을 하나만 반환하고, 그게 결과셋의 새로운 컬럼으로 들어가. 그리고 조건에는 메인_테이블의 컬럼을 참조할 수도 있어.

예제 1: 학생의 평균 점수 추가하기

간단하면서도 실용적인 쿼리부터 시작해보자: students 테이블이랑 학생 점수가 들어있는 grades 테이블이 있다고 치자.

students 테이블:

id name
1 Alex Lin
2 Anna Song
3 Dan Seth

grades 테이블:

student_id grade
1 90
1 85
2 76
3 88
3 92

이제 학생 이름이랑 평균 점수를 같이 뽑고 싶으면 SELECT에서 서브쿼리를 이렇게 써:

SELECT
    s.id,
    s.name,
    (SELECT AVG(g.grade) 
     FROM grades g 
     WHERE g.student_id = s.id) AS average_grade
FROM students s;

결과:

id name average_grade
1 Alex Lin 87.5
2 Anna Song 76.0
3 Dan Seth 90.0

여기서 (SELECT AVG(g.grade) FROM grades g WHERE g.student_id = s.id) 이 서브쿼리가 각 학생의 평균 점수를 계산해줘. students 테이블의 각 행마다 하나씩 값을 반환하니까, JOIN이나 미리 VIEW 만들 필요 없이 바로 쓸 수 있어서 편해.

예제 2: 학생별 수강 과목 개수 세기

이번엔 학생이 몇 개의 과목을 듣고 있는지 데이터도 추가해보자. 이런 테이블이 있다고 치자:

enrollments 테이블:

student_id course_id
1 101
1 102
2 101

학생별로 수강 중인 과목 개수를 뽑으려면 이렇게 하면 돼:

SELECT
    s.id,
    s.name,
    (SELECT COUNT(*)
     FROM enrollments e
     WHERE e.student_id = s.id) AS course_count -- 외부 students 테이블 참조
FROM students s;

결과:

id name course_count
1 Alex Lin 2
2 Anna Song 1
3 Dan Seth 0

서브쿼리 (SELECT COUNT(*) FROM enrollments e WHERE e.student_id = s.id)가 각 학생에 대해 enrollments 테이블에서 몇 개의 레코드가 있는지 세어줘.

서브쿼리에서 데이터 집계하기

실제로 SELECT에서 서브쿼리는 집계 데이터 계산에 자주 써. AVG, SUM, COUNT, MAX, MIN 같은 함수로 다른 쿼리 안에서 바로 데이터 처리할 수 있지.

예제 3: 학생의 총점 구하기

이번엔 각 학생의 총점을 추가해보자. grades 테이블에서 모든 점수를 더하는 서브쿼리를 쓰면 돼:

SELECT
    s.id,
    s.name,
    (SELECT SUM(g.grade)
     FROM grades g
     WHERE g.student_id = s.id) AS total_grade
FROM students s;

결과:

id name total_grade
1 Alex Lin 175
2 Anna Song 76
3 Dan Seth 180

이 서브쿼리 (SELECT SUM(g.grade) FROM grades g WHERE g.student_id = s.id)는 각 학생의 점수를 다 더해줘. 만약 학생이 점수가 하나도 없으면 결과가 NULL이야. SUM 함수는 값이 없으면 NULL을 반환하거든.

제약사항과 팁

  1. 성능. SELECT에서 서브쿼리는 메인 테이블의 각 행마다 따로 실행돼. 데이터가 많으면 엄청 느려질 수 있어. 가능하면 JOIN이나 미리 집계된 데이터를 써봐. 예를 들어:
SELECT
    s.id,
    s.name,
    g.total_grade
FROM students s
LEFT JOIN (
    SELECT student_id, SUM(grade) AS total_grade
    FROM grades
    GROUP BY student_id
) g ON s.id = g.student_id;

이렇게 JOIN을 쓰면 집계랑 계산이 한 번만 일어나서 더 효율적이야.

2. NULL 문제.

서브쿼리에 데이터가 없으면 결과가 NULL이 돼. 이게 의외로 헷갈릴 수 있어. 예시:

SELECT
    s.id,
    s.name,
    (SELECT SUM(g.grade)
     FROM grades g
     WHERE g.student_id = s.id) AS total_grade
FROM students s;

만약 grades에 학생 데이터가 없으면 total_gradeNULL이야. NULL 대신 0을 넣고 싶으면 COALESCE 함수를 써:

SELECT
    s.id,
    s.name,
    COALESCE((SELECT SUM(g.grade)
              FROM grades g
              WHERE g.student_id = s.id), 0) AS total_grade
FROM students s;

여기서 COALESCE 함수의 첫 번째 파라미터로

(
    SELECT SUM(g.grade)
    FROM grades g
    WHERE g.student_id = s.id
)

SELECT에서 서브쿼리 최적화

불필요한 계산을 줄이고 성능을 높이려면:

  1. 서브쿼리에 쓰이는 컬럼에 인덱스를 걸어. 예를 들어 grades 테이블의 student_id에 인덱스가 있으면 필터링이 빨라져.
  2. 가능하면 서브쿼리 대신 미리 집계한 데이터를 JOIN으로 붙여.
  3. 서브쿼리에서 처리하는 데이터 양을 WHERE로 제한해.

마지막 예제: 서브쿼리 합치기

지금까지 배운 걸 다 합쳐서, 학생 이름, 평균 점수, 수강 과목 개수, 총점을 한 번에 뽑아보자:

SELECT
    s.id,
    s.name,
    (SELECT AVG(g.grade)
     FROM grades g
     WHERE g.student_id = s.id) AS average_grade,
    (SELECT COUNT(*) 
     FROM enrollments e 
     WHERE e.student_id = s.id) AS course_count,
    (SELECT SUM(g.grade)
     FROM grades g
     WHERE g.student_id = s.id) AS total_grade
FROM students s;

이 쿼리는 서브쿼리의 힘으로 학생의 전체 프로필을 한 번에 뽑아줘. 평균 점수, 총점, 수강 과목 개수까지 다 나오지. 이런 식으로 하면 별도의 VIEW나 JOIN 없이도 집계 정보를 빠르게 얻을 수 있어서 진짜 편해.

id name average_grade course_count total_grade
1 Alex Lin 87.5 2 175
2 Anna Song 76.0 1 76
3 Dan Seth 90.0 0 180
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION