MySQL

MySQL - GROUP BY 와 HAVING 절

jiyoon12 2025. 5. 16. 17:40

1. GROUP BY

  • SQL에서 특정 열의 값에 따라 행 집합을 요약된 그룹으로 나누는 데 사용한다.
  • 주로 집계 함수(SUM, AVG, MAX, MIN, COUNT 등)와 함께 사용되어 각 그룹에 대한 요약 정보를 제공한다.
  • 데이터를 원하는 그룹으로 나눌 수 있고, 그룹화할 컬럼명을 SELECT 절과 GROUP BY 절에 추가한다.
  • 집계 함수와 함께 사용되는 상수는 GROUP BY 절에 포함하지 않아도 된다.
  • 비집계 컬럼은 GROUP BY에 포함해야 하며, MySQL의 ONLY_FULL_GROUP_BY 모드에서 이를 위반하면 오류 발생한다.

 

 

2. HAVING

  • GROUP BY 절과 함께 사용되며, 특정 조건을 만족하는 그룹만 필터링한다.
  • WHERE 절이 개별 행에 조건을 적용하는 반면, HAVING 절은 그룹화된 결과에 조건을 적용한다.
  • 집계 함수(AVG, COUNT 등)를 조건으로 사용 가능하고, GROUP BY 이후에 작성한다.
  • SELECT 절의 별칭은 MySQL에서 직접 참조가 불가능하다.

3. 주의 사항 

  • WHERE: 개별 행 필터링
  • HAVING: 그룹화 후 필터링.
  • 대량 데이터 처리 시 GROUP BY 컬럼에 인덱스 사용 권장.

  • 테이블 생성과 데이터 삽입
DROP TABLE IF EXISTS tb_student;
-- IF EXISTS : 해당 테이블이 존재할 경우에만 삭제하도록 조건을 거는 옵션

CREATE TABLE tb_student (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    class CHAR(1) NOT NULL,
    score INT NOT NULL
);

INSERT INTO tb_student (name, class, score) VALUES
('김민수', 'A', 85),
('이서연', 'B', 75),
('박지훈', 'A', 65),
('최예린', 'A', 70),
('정하윤', 'B', 95),
('강동현', 'C', 88),
('오소연', 'C', 92),
('한지민', 'B', 78),
('윤태양', 'A', 85),
('문채원', 'C', 90);

 

  • GROUP BY 와 HAVING 절 사용해보기
-- 1.
select class, count(name)
from tb_student
group by class;

-- 2. 각 클래스 ( A, B, C) 의 평균 점수를 계산하고, 소수점 둘째 자리로 반올림 해주세요.
select class , round(avg(score), 2) as avg_score
from tb_student
group by class;


-- 3. 각 클래스 ( A, B, C) 의 평균 점수를 계산하고, 80점 이상인 클래스만 반환해주세요.
-- select * from tb_student where score >= 80;
-- 그룹바이 절에 조건을 추가하고자 한다면 having 절을 사용하면 한다.
select class , round(avg(score), 2) as avg_score
from tb_student
group by class
having avg(score) >= 80;

select class , round(avg(score), 2) as avg_score
from tb_student
group by class
having avg_score >= 80;

  • 테이블 생성과 데이터 삽입
use my_emp_db;
DROP TABLE IF EXISTS tb_employees;

CREATE TABLE tb_employees (
    employee_id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    department VARCHAR(50) NOT NULL,
    salary INT NOT NULL
);

INSERT INTO tb_employees (name, department, salary) VALUES
('김도현', '영업', 48000000),
('이소영', '영업', 55000000),
('박지영', '마케팅', 50000000),
('최민재', '마케팅', 45000000),
('강민호', '인사', 35000000),
('오수진', '인사', 40000000),
('정우성', '개발', 75000000),
('한지은', '개발', 65000000),
('윤서현', '개발', 72000000),
('문태준', '개발', 68000000),
('신동엽', '영업', 52000000),
('장미란', '영업', 51000000),
('황아영', '마케팅', 47000000),
('류현진', '인사', 43000000),
('김나영', '인사', 39000000);

 

  • GROUP BY 와 HAVING 절 사용해보기
-- 부서별 최고 급여를 출력하시오.
select department, max(salary)
from tb_employees
group by department;

-- 직원 수가 4명 이상인 부서를 출력 하시오.
select department, count(*) as emp_count
from tb_employees
group by department
having emp_count >= 4;

-- 부서별 평균 급여(단, 소수점은 다 제거)와 직원 수를 출력하세요.
select department,  round(avg(salary), 0) as avg_salary,
	 count(*) as emp_count
from tb_employees
group by department;

-- 부서별 평균 연봉이 5천만원 이상인 부서 조회
select department, round(avg(salary), 0) as avg_salary
from tb_employees
group by department
having avg_salary >= 50000000;

-- 부서별 연봉 합계가 2억 이상인 부서 조회
select department, sum(salary) as sum_salary
from tb_employees
group by department
having sum_salary >= 200000000;

-- 부서별 최소 연봉이 4천만원 미만인 부서 조회
select department, min(salary) as min_salary
from tb_employees
group by department
having min_salary < 40000000;

  • 연습해보기
-- 실습해보기
select count(*) as c_count
from new_employees;

select sum(salary) as sum_salary
from new_employees;

select max(salary) as amx_salary
from new_employees;

select min(salary) as min_salary
from new_employees;

select concat(upper('mr/ns '), name) as formal_name
from new_employees;

select trim(name)as trim_name
from new_employees;

select *, substring(name, 2, 3) as formal_name
from new_employees;