[SQLD] 04. GROUP BY와 HAVING

TJK·2025년 7월 24일

GROUP BY와 HAVING - 데이터 집계 및 그룹 필터링

개별 행을 조회하는 것을 넘어, 데이터를 특정 기준으로 그룹화하고 요약된 정보를 추출하는 방법을 다룸. 이를 위해 집계 함수(Aggregate Function), GROUP BY 절, HAVING 절이 사용됨.


1. 집계 함수 (Aggregate Functions)

여러 행(Row)의 데이터를 입력받아 단 하나의 요약된 결과 값을 반환하는 함수. GROUP BY 절과 함께 사용되어 그룹별 통계를 내는 데 핵심적인 역할을 함.

함수설명NULL 값 처리
COUNT(컬럼/*)행의 개수를 반환함.COUNT(컬럼)은 해당 컬럼 값이 NULL인 행을 제외하고 계산함. COUNT(*)NULL 값과 관계없이 모든 행의 수를 계산함.
SUM(컬럼)숫자 컬럼의 총합을 반환함.NULL인 행을 무시하고 계산함.
AVG(컬럼)숫자 컬럼의 평균을 반환함.NULL인 행을 무시하고 계산함. (분모에서도 제외됨)
MAX(컬럼)컬럼 값 중 최댓값을 반환함.NULL인 행을 무시하고 계산함.
MIN(컬럼)컬럼 값 중 최솟값을 반환함.NULL인 행을 무시하고 계산함.

※ 실전 팁: AVG(컬럼)SUM(컬럼) / COUNT(컬럼)과 동일한 결과를 반환함. SUM(컬럼) / COUNT(*)와는 결과가 다를 수 있음에 유의해야 함. NULL 값이 포함된 경우 분모가 달라지기 때문임.

-- Post 테이블의 view_count 컬럼에 대한 전체 집계
SELECT
    COUNT(*) AS "전체 게시물 수",
    COUNT(view_count) AS "조회수가 있는 게시물 수",
    SUM(view_count) AS "총 조회수",
    ROUND(AVG(view_count)) AS "평균 조회수",
    MIN(view_count) AS "최저 조회수",
    MAX(view_count) AS "최고 조회수"
FROM
    Post;

2. GROUP BY 절 - 데이터 그룹화

특정 컬럼의 값이 동일한 행들을 하나의 그룹으로 묶어, 그룹별로 집계 함수를 적용할 때 사용함.

  • GROUP BY를 사용하면, SELECT 절에는 GROUP BY에 사용된 컬럼집계 함수만 올 수 있음.
  • 이는 그룹 전체를 대표할 수 없는 일반 컬럼(예: post_id, content)을 SELECT 절에 명시할 경우, 어떤 값을 대표로 출력해야 할지 모호해지기 때문임.
/*
 * -- 사용자별 작성 게시물 수 집계 --
 * Post 테이블의 데이터를 user_id 기준으로 그룹화하고,
 * 각 그룹(사용자)별로 행의 개수를 세어 게시물 수를 계산함.
 */
SELECT
    user_id,
    COUNT(*) AS post_count
FROM
    Post
GROUP BY
    user_id
ORDER BY
    user_id;

/*
 * -- 사용자별, 게시물 종류별 게시물 수 집계 --
 * 두 개 이상의 컬럼을 기준으로 더 세분화된 그룹을 생성할 수 있음.
 */
SELECT
    user_id,
    post_type,
    COUNT(*) AS count_by_type
FROM
    Post
GROUP BY
    user_id, post_type
ORDER BY
    user_id, post_type;

3. HAVING 절 - 그룹 필터링

GROUP BY를 통해 생성된 그룹 결과에 대한 조건을 지정하여, 특정 조건을 만족하는 그룹만 남기는 필터 역할을 함.

  • HAVING 절에는 집계 함수를 사용한 조건식을 사용할 수 있음.
  • WHERE 절은 그룹화 이전에 개별 행을 필터링하는 반면, HAVING 절은 그룹화 이후에 그룹 자체를 필터링함.
/*
 * -- 게시물을 10개 이상 작성한 사용자만 조회 --
 * user_id로 그룹화 후, 그룹별 게시물 수(COUNT(*))가 10 이상인 그룹만 필터링함.
 */
SELECT
    user_id,
    COUNT(*) AS post_count
FROM
    Post
GROUP BY
    user_id
HAVING
    COUNT(*) >= 10;

4. WHEREHAVING의 비교 (SQLD 핵심 포인트)

WHEREHAVING은 둘 다 필터링 역할을 하지만, 적용 대상과 시점이 명확히 다름. 이 차이는 SQLD 시험의 단골 출제 유형임.

항목WHEREHAVING
필터링 대상개별 행 (Row)그룹 (Group)
실행 시점GROUP BY 이전GROUP BY 이후
집계 함수 사용불가능 (COUNT, SUM 등 사용 불가)가능

SQL 쿼리 실행 순서 (논리적 순서)

  1. FROM: 테이블 지정
  2. WHERE: 개별 행 필터링
  3. GROUP BY: 그룹화
  4. HAVING: 그룹 필터링
  5. SELECT: 결과 컬럼 지정
  6. ORDER BY: 최종 결과 정렬
/*
 * -- WHERE와 HAVING 동시 사용 예시 --
 * 내용(content)의 길이가 30자 이상인 게시물들 중에서 (WHERE),
 * 사용자별로 그룹화했을 때 (GROUP BY),
 * 그러한 게시물이 5개 이상인 사용자만 조회 (HAVING).
 */
SELECT
    user_id,
    COUNT(*) AS long_post_count
FROM
    Post
WHERE
    LENGTH(content) >= 30 -- 1. 그룹화 전, 개별 행 필터링
GROUP BY
    user_id               -- 2. 필터링된 행들을 그룹화
HAVING
    COUNT(*) >= 5         -- 3. 그룹화 후, 그룹 결과 필터링
ORDER BY
    user_id;

5. 시험 문제 유형 및 함정 포인트

  • WHERE 절에 집계 함수 사용: WHERE COUNT(*) > 10과 같은 쿼리는 문법 오류임. 집계 함수를 조건으로 사용하려면 반드시 HAVING 절에 작성해야 함.
  • COUNT(*) vs COUNT(컬럼): NULL 값이 있는 컬럼에 대해 두 함수의 결과가 다를 수 있음을 이용한 문제가 출제됨.
  • GROUP BYSELECT 절의 관계: GROUP BY에 사용되지 않은 일반 컬럼이 SELECT 절에 단독으로 올 수 없다는 규칙을 위반한 쿼리를 찾는 문제가 출제됨.
  • 실행 순서 이해: WHEREHAVING의 실행 순서 차이를 정확히 이해하고 쿼리 결과를 예측하는 문제가 나옴. WHERE 조건이 먼저 적용되어 GROUP BY 대상 데이터가 줄어든다는 점을 인지해야 함.
profile
Hello world!

0개의 댓글