오라클에서 정말 많이 사용하는 GROUP BY 졸업하기
일을 하다 보면 데이터가 중복으로 들어가 있는 테이블에서 특정 조건을 만족하는 데이터만 조회하는 경우가 정말 많다.
매번 찾아보기도 힘들고 한번 알아두면 누구보다 빠르게 쿼리를 작성할 수 있기 때문에 처음이자 마지막으로 정리를 하려고 한다.
우선 GROUP BY는 각종 집계함수, 그룹함수와 함께 쓰이며 그룹화된 정보를 제공한다. 데이터를 그룹으로 나누어, 그룹별로 집계된 정보를 출력하고 비교할 때 GROUP BY 가 사용된다.
GROUP BY로 그룹화된 결과에 대해 조건을 걸 수 있는데 이것이 HAVING 절이다. 그룹별로 적용할 수 있는 함수 및 연산자를 사용할 수 있다.
1. HAVING에서 사용할 수 있는 연산자
연산자 | 설명 | 예제 |
= | 특정 값과 같은 그룹 선택 | HAVING COUNT(*) = 10 |
!=, <> | 특정 값과 다른 그룹 선택 | HAVING COUNT(*) <> 5 |
>, <, >=, <= | 특정 값보다 크거나 작은 그룹 선택 | HAVING SUM(salary) > 50000 |
IN | 특정 값 목록에 포함된 그룹 선택 | HAVING department IN ('HR', 'IT') |
NOT IN | 특정 값 목록에 포함되지 않은 그룹 선택 | HAVING department NOT IN ('HR', 'IT') |
BETWEEN | 값의 범위를 지정 | HAVING SUM(sales) BETWEEN 10000 AND 50000 |
LIKE | 특정 패턴과 일치하는 그룹 선택 | HAVING department LIKE 'Tech%' |
IS NULL, IS NOT NULL | 그룹 내 집계 결과가 NULL인지 확인 | HAVING AVG(salary) IS NOT NULL |
2. HAVING에서 사용할 수 있는 집계 함수
HAVING 절에서는 GROUP BY로 묶인 그룹에 대한 집계 함수(Aggregate Function)를 사용할 수 있다.
집계 함수 | 설명 | 예제 |
COUNT(*) | 그룹 내 행 개수 | HAVING COUNT(*) > 5 |
COUNT(column) | 특정 컬럼 값이 NULL이 아닌 행 개수 | HAVING COUNT(department) > 10 |
SUM(column) | 그룹 내 합계 | HAVING SUM(sales) > 50000 |
AVG(column) | 그룹 내 평균 | HAVING AVG(salary) >= 40000 |
MIN(column) | 그룹 내 최소값 | HAVING MIN(salary) > 20000 |
MAX(column) | 그룹 내 최대값 | HAVING MAX(salary) < 100000 |
STDDEV(column) | 그룹 내 표준편차 | HAVING STDDEV(salary) > 10000 |
VARIANCE(column) | 그룹 내 분산 | HAVING VARIANCE(salary) > 5000 |
MEDIAN(column) | 그룹 내 중앙값 (Oracle 전용) | HAVING MEDIAN(salary) > 45000 |
3. HAVING에서 여러 조건을 조합하는 논리 연산자
여러 조건을 AND, OR, NOT 연산자로 조합할 수 있다.
연산자 | 설명 | 예제 |
AND | 여러 조건을 모두 만족해야 함 | HAVING COUNT(*) > 5 AND SUM(sales) > 10000 |
OR | 하나라도 만족하면 선택 | HAVING COUNT(*) > 5 OR SUM(sales) > 10000 |
NOT | 특정 조건을 제외 | HAVING NOT COUNT(*) > 10 |
4. HAVING 절 예제
4-1. COUNT() 사용하여 특정 개수 이상인 그룹만 조회
SELECT department, COUNT(*) AS employee_count
FROM employees
GROUP BY department
HAVING COUNT(*) > 5;
* 각 부서별 직원 수가 5명 이상인 부서만 조회
4-2. SUM() 사용하여 총 매출이 50,000 이상인 그룹만 조회
SELECT region, SUM(sales) AS total_sales
FROM sales_data
GROUP BY region
HAVING SUM(sales) >= 50000;
* 각 지역(region)별 총 매출이 50,000 이상인 그룹만 조회
4-3. AVG() + MAX() 사용
SELECT department, AVG(salary) AS avg_salary, MAX(salary) AS max_salary
FROM employees
GROUP BY department
HAVING AVG(salary) > 40000 AND MAX(salary) < 100000;
* 부서별 평균 급여가 40,000 이상이고, 최대 급여가 100,000 미만인 부서만 조회
4-4. COUNT(DISTINCT column) 사용하여 중복 제거 후 개수 비교
SELECT category, COUNT(DISTINCT product_id) AS unique_products
FROM products
GROUP BY category
HAVING COUNT(DISTINCT product_id) > 10;
* 카테고리별 고유한 상품 개수가 10개 이상인 경우만 조회
4-5. HAVING + ORDER BY 함께 사용
SELECT department, COUNT(*) AS employee_count
FROM employees
GROUP BY department
HAVING COUNT(*) > 5
ORDER BY employee_count DESC;
* 직원 수가 5명 이상인 부서를 조회하면서, 직원 수가 많은 순으로 정렬
COUNT() 함수
오라클에서 COUNT() 함수는 특정 컬럼 또는 표현식의 개수를 세는 집계함수이다.
HAVING COUNT(*) 에서 괄호 안 * 에 들어갈 수 있는 요소를 정리해보자.
1. COUNT(*) (전체 행 개수)
SELECT department, COUNT(*) AS total_count
FROM employees
GROUP BY department
HAVING COUNT(*) > 5;
* 각 부서별 총 직원 수가 5명 이상인 부서만 조회
* NULL 값을 포함한 모든 행을 개수로 셈
2. 특정 컬럼 값 개수( COUNT(column) )
SELECT department, COUNT(salary) AS non_null_salaries
FROM employees
GROUP BY department
HAVING COUNT(salary) > 5;
* 부서별 salary가 NULL이 아닌 행의 개수를 카운트
* NULL 값은 개수에서 제외된다!!!!!!
3. DISTINCT을 사용하여 중복 제거한 개수( COUNT(DISTINCT column) )
SELECT department, COUNT(DISTINCT job_title) AS unique_jobs
FROM employees
GROUP BY department
HAVING COUNT(DISTINCT job_title) > 3;
* 부서별 중복되지 않은 직무(job_title) 개수가 3개 이상인 부서만 조회
* NULL 값은 개수에서 제외된다.
4. 특정 조건을 만족하는 값 개수 ( COUNT(CASE WHEN ...) )
SELECT department,
COUNT(CASE WHEN salary > 50000 THEN 1 END) AS high_salary_count
FROM employees
GROUP BY department
HAVING COUNT(CASE WHEN salary > 50000 THEN 1 END) > 3;
* 급여(salary)가 50,000 이상인 직원 수가 3명 이상인 부서만 조회
* 조건을 만족하지 않는 행은 개수에서 제외됨
5. 다중 도건 사용 ( COUNT(CASE WHEN ...) 여러개 ) ** 이걸 쓸 일이 많다.
SELECT department,
COUNT(CASE WHEN salary > 50000 THEN 1 END) AS high_salary_count,
COUNT(CASE WHEN job_title = 'Manager' THEN 1 END) AS manager_count
FROM employees
GROUP BY department
HAVING COUNT(CASE WHEN salary > 50000 THEN 1 END) > 3
AND COUNT(CASE WHEN job_title = 'Manager' THEN 1 END) > 2;
* 급여 50,000 이상 직원 수가 3명 이상이고, 매니저가 2명 이상인 부서만 조회
6. NULL 값 포함 여부 체크 ( COUNT(NULLIF(column, 조건)) )
SELECT department, COUNT(NULLIF(salary, 0)) AS non_zero_salaries
FROM employees
GROUP BY department
HAVING COUNT(NULLIF(salary, 0)) > 5;
* 급여가 0이 아닌 직원 수가 5명 이상인 부서만 조회
* NULLIF(salary, 0)는 salary가 0이면 NULL 로 변환하여 COUNT 에서 제외시킨다.
7. 조합 사용 (COUNT + SUM/AVG 등)
SELECT department, COUNT(*) AS total_employees, SUM(salary) AS total_salary
FROM employees
GROUP BY department
HAVING COUNT(*) > 10 AND SUM(salary) > 500000;
* 직원 수가 10명 이상이고, 급여 총합이 500,000 이상인 부서만 조회
외울건 아니지만 이런 경우에 이런걸 쓰는구나~ 라는걸 알고 있으면 쿼리를 짤때 도움이 많이 될 것 같다.