개발 창고/Database

[SQL] GROUP BY란? - 기본편

로이제로 2020. 9. 23. 09:58
반응형

 이전 시간에는 SQL의 데이터 조작 언어(DML) 중에서 SELECT의 기본적인 사용법과 결합(JOIN)과 별칭 등에 대해서 다뤄보았습니다. 

 

 이번 시간에는 SELECT의 조건절인 WHERE문 다음에 오는 GROUP BY에 대해 이야기해볼까 합니다. 먼저 오늘은 GROUP BY의 기본적인 사용에 대해 알아볼 것이고, 이후에 그룹 함수와 HAVING, ROLL UP, CUBE 등에 대해 정리해보도록 하겠습니다.

SELECT [ALL|DISTINCT] 컬럼명 [, 컬럼명 ...]
FROM 테이블명 [, 테이블명 ...]
[WHERE 조건식]
[GROUP BY 컬러명 [, 컬럼명 ...] [HAVING 조건식]]
[ORDER BY 컬럼명 [, 컬럼명 ...]

 

 GROUP BY를 한 마디로 정의하자면

 "중복되지 않은 정보를 보여주는 것"

입니다.

 

활용 예를 먼저 보면, (주로 그룹 함수와 함께 사용되는 예)

 

 1. 멜론 차트를 뽑고 싶습니다. (→ 음원별 스트리밍 횟수 내림차순)

 2. 판매 된 상품 중 베스트 상품을 알고 싶습니다. (→ 상품별 주문수량 내림차순)

 3. 자유게시판 게시글 목록에 코멘트 개수랑 같이 보여주세요. (→ 게시글별 코멘트 개수)

 4. 지역별 부동산 매물 수를 알고 싶습니다. (→ 지역별 매물의 개수)

 

등등 많은 예가 있는데 괄호 안을 보시면 "OO별"이라고 적힌 부분이 바로 GROUP BY를 쓰는 이유입니다. 물론 Sub-Query가 더 편리한 경우도 있지만, 이에 대해서는 추후에 다루기로 하겠습니다.

 

일단 오늘의 예시 테이블은 다음과 같습니다. (일관성을 위해 지난 글의 게시글 테이블을 활용할 예정입니다.)

 

  TB_BOARD

 

  TB_BOARD_COMMENT

 

일단 활용 예 중에서 3번을 할 예정인데, 먼저 두 테이블을 LEFT OUTER JOIN으로 묶어줍니다.

 

why?

더보기

게시글 별 코멘트 수

→ 주체는 "게시글 별" → 게시글 번호를 PK로 갖는 마스터 테이블인 TB_BOARD가 기준이 됨

LEFT OUTER JOIN인 이유 → TB_BOARD_COMMENT가 없는 게시글도 코멘트 0개로 보여야 하기 때문

 

만약,

"게시글 목록에 코멘트 개수랑 같이 보여주세요."

 

이 멘트를

"게시글 목록에 코멘트가 있는 애들 보여주세요."

 

라고 ~만이라는 말이 붙었다면 INNER JOIN을 사용하면 됩니다.

 

SELECT *
  FROM TB_BOARD as T1
  LEFT OUTER JOIN TB_BOARD_COMMENT as T2 
    ON T1.BBS_SEQ = T2.BBS_SEQ

 다음과 같이 묶어주면 아래와 같이 각 게시글에 대한 코멘트가 묶인 전체 목록이 노출되게 됩니다.

 

여기부터는 별칭인 Alias를 활용하도록 하겠습니다. 별칭에 대한 내용은 아래에서 자세히 다뤄두었습니다.

2020/09/22 - [개발 창고/데이터베이스 개발] - [SQL] 별칭(Alias) 활용하기

 

 하지만 지금 필요한 것은 자유게시판의 글 목록인데 다음과 같이 BBS_TITLE의 "제목입니다[1]"가 두 개 이상으로 보이면 안 될 겁니다. 이때, GROUP BY를 적용해주면

 

SELECT T1.BBS_SEQ, T1.BBS_TITLE, T1.BBS_CONT, T1.CREATE_DTM, T1.BBS_TYPE_CODE
  FROM TB_BOARD as T1
  LEFT OUTER JOIN TB_BOARD_COMMENT as T2 
    ON T1.BBS_SEQ = T2.BBS_SEQ
 GROUP BY T1.BBS_SEQ, T1.BBS_TITLE, T1.BBS_CONT, T1.CREATE_DTM, T1.BBS_TYPE_CODE

 

 아래와 같이 3줄로 줄어 보일 겁니다. 

 

 

 여기서 주의하실 점은 GROUP BY절에 입력한 컬럼을 반드시 SELECT절에 입력해줄 필요는 없지만, SELECT 절에 이용할 컬럼은 반드시 GROUP BY에 입력해주어야 한다는 점입니다.

 

// 그루핑을 하고 TB_BOARD테이블 전체를 그룹핑하고 BBS_SEQ, BBS_TITLE, BBS_CONT만 사용한 경우
SELECT T1.BBS_SEQ, T1.BBS_TITLE, T1.BBS_CONT
  FROM TB_BOARD as T1
  LEFT OUTER JOIN TB_BOARD_COMMENT as T2 
    ON T1.BBS_SEQ = T2.BBS_SEQ
 GROUP BY T1.BBS_SEQ, T1.BBS_TITLE, T1.BBS_CONT, T1.CREATE_DTM, T1.BBS_TYPE_CODE

 

 

// 그루핑을 한 TB_BOARD 컬럼 내용이 아닌 CREATE_DTM, BBS_TYPE_CODE를 사용한 경우
SELECT T1.BBS_SEQ, T1.BBS_TITLE, T1.BBS_CONT, T1.CREATE_DTM, T1.BBS_TYPE_CODE
  FROM TB_BOARD as T1
  LEFT OUTER JOIN TB_BOARD_COMMENT as T2 
    ON T1.BBS_SEQ = T2.BBS_SEQ
 GROUP BY T1.BBS_SEQ, T1.BBS_TITLE, T1.BBS_CONT

 특이하게도 오라클 등은 이에 대한 문법 오류를 발생시키지만, MySQL 등은 이를 그냥 노출시킵니다. (최근에는 MySQL도 이를 막는다고 합니다.)

더보기

 지금 SQL문은 간단한 편이지만, 복잡한 SQL문에서는 이를 놓치는 고급 개발자들도 빈번합니다. 더군다나 이전 MySQL처럼 오류로 발생하지 않고 결과를 보여주는 경우엔 처음엔 문제없이 쓰는데 내용이 자꾸 이상하게 도출되는 경우가 발생해서 오히려 큰 문제가 발생하기도 합니다. (실제로 영업실적 등을 대표님에게 보고하는 페이지에서 실적이 잘못 그루핑 되어 잘못된 보고가 들어가는걸 본적 있습니다.)

 

 마지막으로  "목록에 코멘트 개수랑 같이"라는 부분을 해결해줘야 할 때입니다. 이때 사용하는 게 바로 그룹 함수인데, 그룹핑된 결과에 대해 그룹핑에 대한 정보들을 표기하는 거라고 보시면 됩니다. 흔히 COUNT, SUM 등이 있는데 이에 대해서는 추후에 자세히 다뤄보도록 하겠습니다. 여기에서는 각 결과 별 그룹핑된 수량이 몇 개인지를 보여주는 COUNT를 활용해보도록 하겠습니다.

 

SELECT T1.BBS_SEQ, T1.BBS_TITLE, T1.BBS_CONT, T1.CREATE_DTM, T1.BBS_TYPE_CODE
     , COUNT(T2.CMNT_SEQ)	as CMNT_CNT
  FROM TB_BOARD as T1
  LEFT OUTER JOIN TB_BOARD_COMMENT as T2 
    ON T1.BBS_SEQ = T2.BBS_SEQ
 GROUP BY T1.BBS_SEQ, T1.BBS_TITLE, T1.BBS_CONT

 

결과는 아래와 같습니다

 

 

 

 각 색상별로 그룹핑이 된 결과이며, 빨간색 CMNT_SEQ의 개수씩 만큼 COUNT 된 결과가 CMNT_CNT에 표현됨을 확인 가능할 겁니다.

 

 여기서 주의하실 점은 COUNT에 CMNT_SEQ를 넣었다는 점입니다. 일반적으로 아래와 같이 COUNT에는 별표(*)를 많이 사용합니다. 하지만 여기에서는 그러면 기준이 되는 TB_BOARD기준으로 COUNT 되기 때문에 요청사항인 코멘트의 수와는 맞지 않습니다.

 

SELECT T1.BBS_SEQ, T1.BBS_TITLE, T1.BBS_CONT, T1.CREATE_DTM, T1.BBS_TYPE_CODE
     , COUNT(*)	as CMNT_CNT
  FROM TB_BOARD as T1
  LEFT OUTER JOIN TB_BOARD_COMMENT as T2 
    ON T1.BBS_SEQ = T2.BBS_SEQ
 GROUP BY T1.BBS_SEQ, T1.BBS_TITLE, T1.BBS_CONT

 

 

위의 결과처럼 3번이 비록 코멘트는 NULL이었지만 게시글이 있기 때문에 0이 아닌 1이 표기됩니다. 때문에 COUNT내에 T2의 값을 넣어줘야 CMNT_CNT가 제대로 0으로 표기됩니다. (COUNT는 NULL을 제외한 개수를 반환하기 때문입니다.)

반응형

'개발 창고 > Database' 카테고리의 다른 글

[SQL] Index란 무엇인가? - 개념편  (0) 2020.11.09
[SQL] GROUP BY란? - 함수편  (0) 2020.09.28
[SQL] 별칭(Alias) 활용하기  (0) 2020.09.22
[SQL] JOIN이란? - CROSS JOIN  (0) 2020.09.17
[SQL] JOIN이란? - OUTER JOIN  (0) 2020.09.16