본문 바로가기
CS/Database

[MySQL] GROUP BY / HAVING 관련 SELECT 컬럼 정리 + 인라인 뷰 (inline view) 활용법

by clolee 2025. 4. 13.

✅ GROUP BY / HAVING 관련 SELECT 컬럼 정리 + 데이터 출력 활용법


📘 1. GROUP BY의 기본 개념 복습

SELECT 컬럼, 집계함수
FROM 테이블
GROUP BY 컬럼;
  • GROUP BY는 특정 컬럼 값을 기준으로 행들을 그룹화
  • 그룹별로 집계함수(Aggregate Function)를 함께 사용

보통 GROUP BY에 사용된 컬럼을 SELECT에 포함시킵니다.

집계 함수만 사용하면 어떤 기준으로 집계된 것인지 알기 어렵기 때문에, GROUP BY 컬럼을 함께 조회하여 무엇에 대한 집계인지 더 명확하게 보여줄 수 있습니다.


✅ 2. SELECT 절에 올 수 있는 컬럼 종류 (GROUP BY 사용 시)

SELECT에 올 수 있는 컬럼 종류 조건 예시
① GROUP BY에 명시된 컬럼 가능 SELECT department
② 집계 함수로 계산된 컬럼 가능 SELECT COUNT(*), SUM(salary)
③ 집계된 결과 기반의 표현식 가능 SELECT ROUND(AVG(score), 1)
④ 상수 값 가능 SELECT '기본값' AS type
⑤ GROUP BY에 없고 집계도 안 된 컬럼 ❌ 오류 발생 (ONLY_FULL_GROUP_BY 설정 시)  

❗ 예외 규칙

-- 잘못된 예 (MySQL 5.7 이상에서는 오류)
SELECT department, name, COUNT(*)
FROM employees
GROUP BY department;

name은 GROUP BY에 포함되어 있지도 않고 집계되지도 않아서 모호한 값 발생


✅ 3. 자주 사용하는 집계 함수

함수 설명
COUNT(*), COUNT(col) 행 수, NULL 제외한 수
SUM(col) 총합
AVG(col) 평균
MIN(col), MAX(col) 최소/최대 값
SELECT department, COUNT(*) AS 인원수, AVG(salary) AS 평균급여
FROM employees
GROUP BY department;

✅ 4. SELECT 예시: 집계 + 표현식 + 정렬 + 필터링

SELECT
    category,
    COUNT(*) AS 제품수,
    ROUND(AVG(price), 2) AS 평균가,
    MAX(price) AS 최고가,
    MIN(price) AS 최저가
FROM products
WHERE status = 'active'
GROUP BY category
HAVING COUNT(*) >= 5
ORDER BY 평균가 DESC;

📌 기능 설명:

  • WHERE: 그룹화 전 필터링
  • GROUP BY: 카테고리별 묶기
  • HAVING: 그룹화 결과 필터
  • SELECT: 그룹 기준 + 집계 결과 + 표현식 출력
  • ORDER BY: 정렬 기준은 집계 컬럼 별칭 가능

✅ 5. SELECT에서 표현식 사용 가능 예시

SELECT
    region,
    COUNT(*) AS 인구수,
    SUM(CASE WHEN gender = 'M' THEN 1 ELSE 0 END) AS 남성수,
    SUM(CASE WHEN gender = 'F' THEN 1 ELSE 0 END) AS 여성수,
    ROUND(SUM(income) / COUNT(*), 2) AS 1인당수입
FROM citizens
GROUP BY region
HAVING SUM(income) > 1000000;

📌 핵심 포인트:

  • CASE WHEN을 써서 조건별 집계 가능
  • ROUND, /, + 등 수학식도 SELECT 내 자유롭게 사용 가능

✅ 6. HAVING 절의 필터링 대상

  • GROUP BY 이후 집계된 값을 필터링
  • SELECT 절에 별칭으로 지정한 컬럼도 HAVING에서 사용 가능
SELECT department, COUNT(*) AS 인원수
FROM employees
GROUP BY department
HAVING 인원수 >= 5;

✔️ HAVING COUNT(*) >= 5 도 가능

✔️ HAVING 인원수 >= 5 도 가능


✅ 7. 실무 예제 정리

📍 예제 1: 고객 등급별 평균 주문 금액

SELECT grade,
       COUNT(*) AS 주문수,
       ROUND(AVG(total_amount), 0) AS 평균주문금액
FROM orders
GROUP BY grade
HAVING 평균주문금액 > 100000;

📍 예제 2: 월별 판매 통계

SELECT
    DATE_FORMAT(order_date, '%Y-%m') AS 월,
    COUNT(*) AS 주문수,
    SUM(amount) AS 총판매액
FROM orders
GROUP BY 월
ORDER BY 월;

📍 예제 3: 다중 컬럼 그룹화

SELECT
    department, gender,
    COUNT(*) AS 인원수
FROM employees
GROUP BY department, gender;

✅ 8. 성능 및 실무 팁

설명
GROUP BY 대상은 인덱스가 있을수록 빠름 특히 많은 그룹일 경우 효과 큼
HAVING은 WHERE로 대체할 수 있으면 WHERE로 성능 향상
SELECT에서 불필요한 컬럼 제외 ONLY_FULL_GROUP_BY 오류 방지
GROUP + 집계 후 필터링은 무조건 HAVING WHERE은 개별 행 필터링용

✅ 9. 결론 요약

SELECT 절에서 가능한 항목 (GROUP BY 사용 시) 비고
GROUP BY에 있는 컬럼 OK
집계 함수 결과 OK
집계 결과 + 표현식 OK
고정값 OK
집계 없는 컬럼 ❌ 오류 발생 가능

GROUP BY를 사용한 후 모든 컬럼을 SELECT하고 싶을 때, 바로 FROM절에 서브쿼리를 써서 해결하는 게 가장 일반적이다.
이걸 "인라인 뷰 (inline view)" 또는 "서브쿼리 테이블화" 전략이라고 함.


✅ 왜 GROUP BY 후 전체 컬럼을 SELECT할 수 없는가?

MySQL은 GROUP BY 사용 시,

SELECT 절에 나오는 모든 컬럼은 아래 중 하나여야 해:

  1. GROUP BY에 명시된 컬럼
  2. 집계함수로 묶인 컬럼
  3. 위 둘을 기반으로 만든 표현식

❌ 예시: 오류 발생

SELECT *
FROM employees
GROUP BY department;
  • name, salary, hire_date 등도 포함되는데,
  • 이 컬럼들은 GROUP BY나 집계 대상이 아님
  • MySQL 5.7 이상에서는 ONLY_FULL_GROUP_BY 옵션 때문에 오류 발생

✅ 해결 방법: FROM절 서브쿼리 활용

SELECT *
FROM (
    SELECT department,
           MIN(name) AS name,  -- 집계 방식 선택
           MAX(salary) AS top_salary,
           COUNT(*) AS 인원수
    FROM employees
    GROUP BY department
) AS grouped_data;

✔️ 이 방법은 GROUP BY 결과를 하나의 테이블처럼 만들어

그 안에서 SELECT * 가능하게 만든다.


📌 실전 팁: 서브쿼리 + 조인 조합도 자주 씀

예를 들어, 부서별 최고 연봉자의 이름, 연봉, 입사일을 전부 보고 싶을 때:

-- 1단계: 부서별 최고 연봉을 먼저 서브쿼리로 추출
SELECT e.*
FROM employees e
JOIN (
    SELECT department, MAX(salary) AS top_salary
    FROM employees
    GROUP BY department
) AS t
ON e.department = t.department AND e.salary = t.top_salary;

✔️ 이건 집계 결과와 원본 데이터를 JOIN해서 전체 컬럼 조회하는 방식.

✔️ 실무에서 아주 많이 씀 (요약 + 상세 데이터 같이 보여줄 때)


📌 요약 정리

하고 싶은 일 방법
GROUP BY 후 전체 컬럼 SELECT ❌ 직접 불가능
해결 ① FROM절에 서브쿼리로 집계 결과 만들고 SELECT * 사용
해결 ② 집계 결과를 JOIN으로 원본 테이블과 결합

댓글