CodeGym /행동 /SQL SELF /HAVING에서 서브쿼리로 집계 데이터 필터링하기

HAVING에서 서브쿼리로 집계 데이터 필터링하기

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

가끔은 단순히 데이터를 그룹화하고 결과를 필터링하는 것만으론 부족할 때가 있어. 예를 들어, 학생 그룹의 평균 점수를 외부 기준이랑 비교하고 싶을 때 말이지. 이럴 때 HAVING이랑 서브쿼리가 등장해 — SQL 쿼리 안에서 더 똑똑한 결정을 내릴 수 있게 해주는 강력한 도구야.

HAVING 다시 보기

이번엔 HAVING이랑 같이 쓰는 서브쿼리에 집중해보자. 이걸로 집계된 값 레벨에서 데이터를 필터링할 수 있어. 왜냐고? WHERE는 개별 행을 필터링하지만, HAVING은 이미 그룹화된 데이터에 적용돼 — 분석의 레벨이 달라서 너의 가능성을 확장시켜주지.

서브쿼리랑 HAVING 조합에 들어가기 전에, HAVING이 뭔지, WHERE랑 뭐가 다른지 잠깐 복습하자.

  • WHERE그룹화(GROUP BY) 전에 행을 필터링해.
  • HAVING집계 후에, 이미 그룹화된 데이터에 필터를 거는 거야.

예를 들어, 학생들이랑 그들의 점수를 분석한다고 해보자. WHERE로는 특정 최소 점수 이하인 학생을 제외할 수 있고, HAVING으로는 평균이나 최대 점수를 기준으로 학생 그룹 전체를 제외할 수 있어.

데이터 예시

학생 예시 테이블이야:

students 테이블:

student_id student_name department grade
1 Alex Physics 80
2 Maria Physics 85
3 Dan Math 90
4 Lisa Math 60
5 John History 70

HAVING 사용 예시 (서브쿼리 없이)

SELECT department, AVG(grade) AS avg_grade
FROM students
GROUP BY department
HAVING AVG(grade) > 75;

결과:

department avg_grade
Physics 82.5
Math 75.0

"History" 학과는 평균 점수가 75보다 낮아서 결과에 안 나와. 간단하지? 이제 서브쿼리로 살짝 마법을 더해보자. 다음 예시에서는 모든 학과의 전체 평균이랑 비교해서 필터링할 수 있어.

HAVING에서 서브쿼리

HAVING에서 서브쿼리를 쓰면 집계 데이터 필터링에 유연성이 확 올라가. 평균 점수나 최대값 같은 집계값을 DB의 다른 부분에서 계산한 값이랑 비교할 수 있지. 쉽게 말해, "우리 결과가 전체 평균보다 나은가?" 이런 것도 체크할 수 있어.

예시: 학과별 평균 점수로 필터링

예를 들어, 학생들이 다른 학과보다 더 잘하는 학과 — 즉, 학과 평균 점수가 전체 평균보다 높은 학과를 찾고 싶다고 해보자.

데이터는 이래:

students 테이블:

student_id student_name department grade
1 Alex Physics 80
2 Maria Physics 85
3 Dan Math 90
4 Lisa Math 60
5 John History 70

먼저 전체 학생 평균 점수를 구해보자:

SELECT AVG(grade) AS university_avg
FROM students;

이제 HAVING에 서브쿼리를 넣어보자:

SELECT department, AVG(grade) AS avg_grade
FROM students
GROUP BY department
HAVING AVG(grade) > (SELECT AVG(grade) FROM students);

결과:

department avg_grade
Physics 82.5

여기서 무슨 일이 일어나는지 보자:

  1. 서브쿼리 (SELECT AVG(grade) FROM students)가 전체 평균 점수를 계산해 — 여기선 77이야.
  2. 메인 쿼리는 학과별로 학생을 그룹화하고 각 학과의 평균 점수를 구해.
  3. HAVING이 학과 평균이 전체 평균보다 높은 학과만 남겨줘.

WHEREHAVING 비교

차이를 이해하려면, 평균보다 점수가 높은 학생만 뽑고 싶다고 해보자. 이건 WHERE만으로 가능해:

SELECT name, grade
FROM students
WHERE grade > (SELECT AVG(grade) FROM students);

결과 (앞의 테이블 기준):

name grade
Alex 80
Maria 85
Dan 90

근데 학과별로 평균 점수가 전체 평균보다 높은 학과를 보고 싶으면 HAVING이 필수야 — 왜냐면 행이 아니라 그룹을 필터링하니까:

SELECT department, AVG(grade) AS avg_grade
FROM students
GROUP BY department
HAVING AVG(grade) > (SELECT AVG(grade) FROM students);

결과:

department avg_grade
Physics 82.5

요약:

  • WHERE는 그룹화 전에 개별 행에 적용돼.
  • HAVING은 집계 후 그룹에 적용돼.

예시: 여러 집계값으로 작업하기

또 다른 케이스를 보자. students 테이블에 학생 점수와 학과 정보가 있다고 해보자:

students 테이블:

name grade department
Alex 80 Physics
Maria 85 Physics
Dan 90 Math
Olga 95 Math
Ivan 70 History
Nina 75 History

이제 이런 학과를 찾고 싶어:

  1. 학생 평균 점수가 전체 평균보다 높고,
  2. 학과 내 최고 점수가 90보다 높아야 해.

이렇게 쿼리를 짜면 돼:

SELECT department, AVG(grade) AS avg_grade, MAX(grade) AS max_grade
FROM students
GROUP BY department
HAVING AVG(grade) > ( SELECT AVG(grade) FROM students )
   AND MAX(grade) > 90;

이 쿼리에서 일어나는 일:

  • AVG(grade) > (SELECT AVG(grade) FROM students) — 학과가 평균적으로 더 잘하는지 체크.
  • MAX(grade) > 90 — 학과에 90점 넘는 학생이 있는지 체크.

결과:

department avg_grade max_grade
Math 92.5 95

"Math" 학과만이 평균도 높고, 90점 넘는 학생도 있는 유일한 학과야.

예시: 최소 편차 그룹 찾기

이번엔, 학생들 중에서 최고점과 최저점의 차이가 전체 대학보다 작은 그룹을 찾고 싶다고 해보자.

students 테이블을 쓸 거야:

name grade department
Alex 80 Physics
Maria 85 Physics
Dan 90 Math
Olga 95 Math
Ivan 70 History
Nina 75 History

단계를 나눠서 해보자:

  1. 먼저 전체 대학에서 최고-최저 점수 차이를 구해:
    SELECT MAX(grade) - MIN(grade) AS range_university
    FROM students;
    
  2. 이제 메인 쿼리랑 이 서브쿼리를 합쳐보자:
SELECT department, MAX(grade) - MIN(grade) AS range_department
FROM students
GROUP BY department
HAVING (MAX(grade) - MIN(grade)) < ( SELECT MAX(grade) - MIN(grade) FROM students );

쿼리 결과:

department range_department
Physics 5
Math 5

"Physics"랑 "Math" 그룹은 점수 편차가 전체 대학보다 더 작아서 더 안정적인 점수를 보여줬어.

HAVING과 서브쿼리 쿼리 최적화

서브쿼리를 중첩해서 쓰면, 특히 큰 데이터베이스에서는 성능에 꽤 영향이 갈 수 있어. 몇 가지 팁을 줄게:

인덱스 사용하기. 서브쿼리가 WHEREJOIN에 쓰이는 컬럼에서 실행된다면, 그 컬럼에 인덱스가 있는지 꼭 확인해.

데이터 과다 방지. 서브쿼리가 너무 많은 중간 결과를 반환하면, 쿼리를 단계별로 나누거나 임시 테이블을 써봐.

EXPLAIN으로 쿼리 프로파일링. PostgreSQL이 쿼리를 어떻게 실행하는지 항상 체크해. 서브쿼리가 여러 번 실행된다면, 최적화를 고민해봐.

CTE랑 비교해보기. 어떤 경우엔 WITH (Common Table Expressions)를 쓰는 게 더 빠르고 읽기 쉬울 수도 있어. 이건 다음 강의에서 다룰게 :P

서브쿼리, HAVING, GROUP BY 조합하기

HAVING에서 서브쿼리를 쓰면, 집계값, 평균, 다른 메트릭을 동시에 고려하는 복잡한 필터도 만들 수 있어. 이걸로 실제 데이터에서 흥미로운 인사이트를 찾을 수 있지.

예시: 학과별 평균 점수와 학생 수 비교

예를 들어, 이런 학과를 뽑고 싶다고 해보자:

  1. 평균 점수가 전체 평균보다 높고,
  2. 학생 수가 평균이 가장 낮은 학과보다 많아야 해.

이게 students 테이블이야:

name grade department
Alex 80 Physics
Maria 85 Physics
Dan 90 Math
Olga 95 Math
Ivan 70 History
Nina 75 History
Oleg 60 History

쿼리:

SELECT department, AVG(grade) AS avg_grade, COUNT(*) AS student_count
FROM students
GROUP BY department
HAVING AVG(grade) > ( SELECT AVG(grade) FROM students )
   AND COUNT(*) > (
       SELECT COUNT(*)
       FROM students
       GROUP BY department
       ORDER BY AVG(grade)
       LIMIT 1
   );

이 쿼리는 HAVINGGROUP BY에서 서브쿼리를 조합해서 여러 기준으로 분석할 수 있다는 걸 보여줘. 결과:

department avg_grade student_count
Physics 82.5 2
Math 92.5 2

History 학과는 평균 점수도 제일 낮고 학생 수도 제일 적어서 결과에 안 나와. Physics랑 Math는 평균 점수도 높고 학생 수도 많아.

흔한 실수와 예방법

NULL 관련 실수. 데이터에 NULL이 있으면, HAVING의 서브쿼리가 예상치 못한 결과를 줄 수 있어. 이런 경우엔 COALESCE로 처리해:

SELECT AVG(grade)
FROM students 
WHERE grade IS NOT NULL;

서브쿼리에서 불필요한 데이터. 서브쿼리가 너무 많은 결과를 반환하면 성능이 떨어져. 항상 서브쿼리 조건을 명확히 해.

실행 순서 오해. HAVING그룹화 후에 실행되고, 서브쿼리는 메인 쿼리보다 먼저 실행될 수도 있다는 걸 기억해.

인덱스 없음. 서브쿼리에 쓰이는 컬럼에 인덱스가 없으면 쿼리가 엄청 느려질 수 있어.

HAVING에서 서브쿼리를 쓰면 집계 레벨에서 데이터 분석이 훨씬 다양해져. 복잡한 조건으로 그룹을 필터링하고, 그룹 간 결과를 비교하고, 복잡한 분석 쿼리도 만들 수 있지. 축하해, 이제 이걸 실제 프로젝트에 쓸 준비가 된 거야!

코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION