개별 행을 조회하는 것을 넘어, 데이터를 특정 기준으로 그룹화하고 요약된 정보를 추출하는 방법을 다룸. 이를 위해 집계 함수(Aggregate Function), GROUP BY 절, HAVING 절이 사용됨.
여러 행(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;
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;
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;
WHERE와 HAVING의 비교 (SQLD 핵심 포인트)WHERE와 HAVING은 둘 다 필터링 역할을 하지만, 적용 대상과 시점이 명확히 다름. 이 차이는 SQLD 시험의 단골 출제 유형임.
| 항목 | WHERE 절 | HAVING 절 |
|---|---|---|
| 필터링 대상 | 개별 행 (Row) | 그룹 (Group) |
| 실행 시점 | GROUP BY 이전 | GROUP BY 이후 |
| 집계 함수 사용 | 불가능 (COUNT, SUM 등 사용 불가) | 가능 |
SQL 쿼리 실행 순서 (논리적 순서)
FROM: 테이블 지정WHERE: 개별 행 필터링GROUP BY: 그룹화HAVING: 그룹 필터링SELECT: 결과 컬럼 지정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;
WHERE 절에 집계 함수 사용: WHERE COUNT(*) > 10과 같은 쿼리는 문법 오류임. 집계 함수를 조건으로 사용하려면 반드시 HAVING 절에 작성해야 함.COUNT(*) vs COUNT(컬럼): NULL 값이 있는 컬럼에 대해 두 함수의 결과가 다를 수 있음을 이용한 문제가 출제됨.GROUP BY와 SELECT 절의 관계: GROUP BY에 사용되지 않은 일반 컬럼이 SELECT 절에 단독으로 올 수 없다는 규칙을 위반한 쿼리를 찾는 문제가 출제됨.WHERE와 HAVING의 실행 순서 차이를 정확히 이해하고 쿼리 결과를 예측하는 문제가 나옴. WHERE 조건이 먼저 적용되어 GROUP BY 대상 데이터가 줄어든다는 점을 인지해야 함.