AD JB Toolbox 01

MariaDB 강좌 / GROUP BY·HAVING으로 묶고 집계 결과 걸러내기

GROUP BY는 같은 기준값을 가진 행을 묶어 그룹별 집계를 만들고 HAVING은 집계한 그룹을 조건으로 거릅니다. WHERE는 묶기 전 개별 행을 거르므로 두 절을 바꾸어 쓰면 집계 대상과 결과가 달라집니다.

한 결과 행이 무엇을 대표하는지 정하기

상품별 조회에서는 한 행이 상품 하나지만 분류별 집계에서는 한 행이 분류 하나입니다. SELECT에 넣는 비집계 열이 그 그룹을 대표할 수 있는지 먼저 확인합니다. 분류·상품명을 함께 골라 놓고 분류만 GROUP BY하면 어떤 상품명을 반환해야 할지 정의되지 않습니다. 각 집계 함수의 NULL 처리와 빈 집합 결과는 COUNT·SUM·AVG·MIN·MAX로 집계하기, 같은 그룹 안의 조건별 집계는 CASE로 조건별 값과 집계 만들기와 함께 비교할 수 있습니다.

절 대상 예
WHERE 원본의 개별 행 stock > 0
GROUP BY 같은 기준의 행 집합 category
HAVING 만들어진 그룹 COUNT(*) >= 2
ORDER BY 최종 출력 순서 category ASC

분류별 수량과 금액 집계 준비하기

학습용 MariaDB 서버의 shopcourse에 course_user로 접속합니다. 명령줄 클라이언트와 UTF-8 SQL 파일 실행 방법대로 코드를 UTF-8 파일에 저장하고 SOURCE C:/mariadb-lab/lesson.sql;처럼 실제 파일 경로로 실행할 수 있습니다. SQL 문은 운영체제 터미널이 아닌 MariaDB 연결 안에서 실행합니다.

먼저 SELECT VERSION(), DATABASE();와 SHOW TABLES;로 대상 서버·DB·이름 중복을 확인합니다. 아래 products_23_lab는 이 글의 독립된 연습용 이름입니다. 이미 존재하면 정의와 행을 확인하고, 보관할 자료가 없는 이 글의 실습 테이블임이 확실할 때만 마지막 절의 정리 순서로 지운 뒤 다시 만듭니다. 기존 강좌의 동명 없는 테이블이나 운영 데이터는 삭제하지 않습니다.

USE shopcourse;
CREATE TABLE products_23_lab (
  product_id INT NOT NULL AUTO_INCREMENT,
  product_name VARCHAR(100) NOT NULL,
  category VARCHAR(30) NOT NULL,
  price DECIMAL(10,2) NOT NULL,
  stock INT NOT NULL DEFAULT 0,
  note VARCHAR(100) NULL DEFAULT NULL,
  PRIMARY KEY (product_id),
  CONSTRAINT ck_p23_price CHECK (price >= 0),
  CONSTRAINT ck_p23_stock CHECK (stock >= 0)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO products_23_lab (product_id,product_name,category,price,stock,note) VALUES (1, '노트', '문구', 3500.00, 20, NULL),
  (2, '볼펜', '문구', 1500.00, 0, ''),
  (3, '머그컵', '생활', 8000.00, 12, '선물 포장'),
  (4, '스탠드', '생활', 25000.00, 5, NULL),
  (5, '파일', '문구', 1500.00, 50, ''),
  (6, '케이블', '전자', 8000.00, 0, '호환 확인');

문구 상품 세 개, 생활 상품 두 개, 전자 상품 한 개로 구성되어 있습니다. 0인 재고를 포함할지에 따라 상품 수가 달라지므로 첫 조회는 WHERE 없이 전체 상품을 집계합니다. SELECT의 열·별칭·계산식에서 출력 열의 의미를 함께 살펴볼 수 있습니다.

GROUP BY로 분류별 결과 만들기

SELECT category,COUNT(*) AS product_count,SUM(stock) AS total_stock,
 SUM(price*stock) AS inventory_value
FROM products_23_lab GROUP BY category ORDER BY category;
분류 상품 수 재고 합 재고 금액
문구 3 70 145000.00
생활 2 17 221000.00
전자 1 0 0.00

같은 분류의 각 상품을 한 그룹으로 묶고 COUNT·SUM을 따로 적용했습니다. GROUP BY 자체의 관찰된 출력 순서에 의존하지 않고 ORDER BY로 원하는 분류 순서를 명시합니다. 문자열 그룹의 일치 여부에는 열의 collation이 영향을 줄 수 있습니다. GROUP BY와 HAVING 공식 설명을 참고하세요.

HAVING으로 그룹의 집계값 제한하기

SELECT category,COUNT(*) AS product_count
FROM products_23_lab
GROUP BY category HAVING COUNT(*) >= 2 ORDER BY category;

결과는 문구 3개와 생활 2개입니다. 그룹 안의 상품 수를 기준으로 고르는 조건이므로 HAVING에 COUNT(*)를 사용합니다. 집계 전에 아직 존재하지 않는 COUNT 결과를 WHERE에 넣는 방식으로 쓰지 않습니다. 별칭을 사용할 수 있는 문법도 있지만 처음에는 집계식을 그대로 써 역할을 드러내는 편이 좋습니다.

WHERE를 먼저 적용하면 무엇이 달라지는지 비교하기

SELECT category,COUNT(*) AS in_stock_products
FROM products_23_lab WHERE stock > 0
GROUP BY category HAVING COUNT(*) >= 2 ORDER BY category;

재고가 있는 행만 먼저 고르면 문구의 볼펜과 전자의 케이블이 제외됩니다. 이후 그룹 수는 문구 2개·생활 2개이고 전자 그룹은 만들어지지 않습니다. HAVING은 사라진 원본 행을 다시 가져오지 않으므로 전체 상품 수와 재고 보유 상품 수를 같은 지표처럼 비교하지 않습니다.

SELECT category,product_name,SUM(stock)처럼 그룹 안에서 하나로 결정되지 않는 열을 넣으면 ONLY_FULL_GROUP_BY 모드에서 오류가 납니다. 해당 모드에서 통과하지 못하는 쿼리를 고치려고 모드를 끄기보다, 상품명까지 그룹 기준에 넣어 결과 단위를 바꿀지 또는 그룹별 집계만 표시할지 정합니다.

NULL·중복·그룹 수를 점검하고 정리하기

그룹 기준 열이 NULL이면 NULL 값끼리 하나의 그룹으로 집계됩니다. WHERE의 NULL 비교와 그룹 묶음은 같은 규칙으로 해석할 수 없습니다. JOIN 뒤에 집계한다면 결합으로 행이 늘어났는지도 먼저 확인하세요. 작은 예제에서는 분류별 원본 행을 출력해 그룹 결과와 일대일로 대조하는 방법이 도움이 됩니다.

반복하기 전 현재 DB와 실제 정의·행을 확인하고 필요한 자료를 백업합니다. 보관할 데이터가 없는 이 글의 전용 실습 객체만 아래 순서로 정리합니다. 외래키가 있으면 자식부터 지웁니다. DROP TABLE은 구조와 데이터를 모두 없애며 일반적인 ROLLBACK으로 복원되지 않으므로 취소 가능한 DML 실습과 분리합니다.

DROP TABLE shopcourse.products_23_lab;
같은 카테고리의 다른 글
MariaDB 강좌 / Docker로 MariaDB 실습 환경 만들기

MariaDB 강좌 / Docker로 MariaDB 실습 환경 만들기

Compose 파일로 MariaDB 포트·암호 파일·데이터 볼륨·헬스체크를 구성합니다. 연결 확인과 컨테이너 교체 실습을 통해 데이터 유지, 초기화와 접속 오류의 차이를 이해합니다.

MariaDB 강좌 / CSV 가져오기·내보내기: 경로·권한·문자 인코딩

MariaDB 강좌 / CSV 가져오기·내보내기: 경로·권한·문자 인코딩

CSV를 문자형 stage에 적재해 형식·중복·경고를 확인한 뒤 실제 타입으로 옮깁니다. LOCAL의 클라이언트 경로, 서버 OUTFILE의 권한·경로, 쉼표·한글·줄바꿈과 왕복 검증을 설명합니다.

MariaDB 강좌 / NULL을 올바르게 비교하기: IS NULL·COALESCE

MariaDB 강좌 / NULL을 올바르게 비교하기: IS NULL·COALESCE

NULL·빈 문자열·0을 구분하고 IS NULL·IS NOT NULL과 COALESCE의 역할을 실습합니다. 일반 비교에서 빠지는 NULL 행, NULL-safe 비교와 NOT IN 목록의 함정을 설명합니다.

MariaDB / 피벗 테이블 만드는 방법

MariaDB / 피벗 테이블 만드는 방법

피벗 테이블은 엑셀의 유용한 기능 중의 하나입니다. 예를 들어 아래의 왼쪽 데이터를 오른쪽 표처럼 만드는 것이 피벗 테이블입니다. 조건에 따라 그룹화시켜서 보기 좋게 데이터를 정리하는 것이라 보면 됩니다. MariaDB에서도 몇 가지 함수를 이용하여 피벗 테이블을 만들 수 있습니다.

MariaDB 강좌 / Python에서 MariaDB 연결하고 매개변수 쿼리 사용하기

MariaDB 강좌 / Python에서 MariaDB 연결하고 매개변수 쿼리 사용하기

PyMySQL로 연결·삽입·조회·확정·취소를 수행하는 전체 Python 예제를 제공합니다. 따옴표·한글 입력과 SQL 인젝션 형태의 문자열을 값으로 다루고 정렬 열은 허용 목록으로 제한합니다.

MariaDB 강좌 / 조회 조건·정렬·페이지 나누기를 개선하기

MariaDB 강좌 / 조회 조건·정렬·페이지 나누기를 개선하기

날짜 범위·정렬·키셋 페이지 조회를 같은 주문 데이터로 검증합니다. 함수 조건의 버전별 최적화, 동점 비교 방향, 포함 검색의 의미와 실제 계획·측정 기준을 설명합니다.

MariaDB 강좌 / mariadb-dump로 데이터베이스 백업하기

MariaDB 강좌 / mariadb-dump로 데이터베이스 백업하기

별도 주문 실습 DB를 준비해 mariadb-dump로 새 SQL 백업 파일을 만듭니다. 종료 코드·해시·문자 인코딩, InnoDB 스냅샷과 동시 DDL의 한계, 객체·권한·별도 복원 검증 기준을 설명합니다.

MariaDB 강좌 / 저장 프로시저·함수·트리거의 차이와 사용 방법

MariaDB 강좌 / 저장 프로시저·함수·트리거의 차이와 사용 방법

프로시저·함수·트리거의 호출 방식과 권한 차이를 재고·금액·변경 기록 예제로 비교합니다. 전체 생성 SQL과 실행 결과, 롤백·확정·오류 확인 및 객체 정리 방법을 제공합니다.

MariaDB 강좌 / 인덱스 만들기: 단일·복합 인덱스와 열 순서

MariaDB 강좌 / 인덱스 만들기: 단일·복합 인덱스와 열 순서

기존 키를 조회한 뒤 고객·날짜 복합 인덱스를 만들고 같은 조회 결과를 확인합니다. 열 순서·범위·커버링, 쓰기 비용·중복 키·외래키 지원 인덱스의 주의점을 설명합니다.

MariaDB 강좌 / 명령줄 클라이언트로 접속하고 SQL 파일 실행하기

MariaDB 강좌 / 명령줄 클라이언트로 접속하고 SQL 파일 실행하기

터미널 명령과 SQL 입력 위치를 구분하고 학습 계정으로 접속합니다. UTF-8 SQL 파일의 SOURCE 실행, 예상 결과, 오류 중단·한글 표시·경로 문제를 함께 확인합니다.