[MySQL] GROUP BY 왜 안 되는거야?

조예진·2022년 5월 10일
4
post-thumbnail

🔎 들어가기 전


갑자기 잘 작동하던 프로시저가 sql_mode 활성화 후 error가 났다.
찾아보니까 Group by가 문제라고 하는데
잘 되던 프로시저가 왜....???? 라는 의문이 들어 이슈를 해결하다가
같은 이슈를 직면한 분들에게 도움이 될까 포스팅해보았다.

결론부터 보고 싶으신 분들은 GROUP BY 개념은 패스하세요:)

📍GROUP BY, HAVING


📌 개념

조건에 따라 그룹화하여 집계를 내거나(GROUP BY), 그룹화 내에 특정 조건으로 추출하고 싶을 때 (HAVING)사용

📌 간단한 사용법

1. 사용법

ex) 각 학년별 총 학생수를 구하고 싶다.

	SELECT 
    	COUNT(학생고유번호)
    FROM 학교
    GROUP BY 학년

ex) 각 학년별 총 학생수를 구하고 싶다 (단, 3학년 미만 학년만 조회)

	SELECT 
    	COUNT(학생고유번호)
    FROM 학교
    GROUP BY 학년
    HAVING 학년 < 3

2. GROUP BY 와 함께 사용되는 집계함수

  • COUNT: 총 갯수
  • COUNT(DISTINCT): 중복없이 총 갯수
  • SUM: 합계
  • AVG: 평균
  • MAX: 최대
  • MIN: 최소

📍GROUP BY 뭐가 문제야?

💡 sql_mode 옵션 중 only_full_group_by로 인해 생긴 이슈


MySQL(v5.7 이후) 버전업되면서 SQL 문법이 이전보다 엄격해졌다.

회사에서 sql_mode를 안켜고 작업을 하다가
최근들어 SQL 모드를 킨 DB서버가 생겼고, 그 후 당연하게 쓰면서 잘못 된 걸 몰랐던 이슈들이 빠방 터졌다.

그 중 아래와 같은 ERROR를 내뱉는 쿼리가 있었다.

Expression #2 of SELECT list is not in GROUP BY clause and contains 
nonaggregated column '컬럼명' which is not functionally dependent on columns in GROUP BY clause; 
this is incompatible with sql_mode=only_full_group_by

마지막 문장을 보면

'this is incompatible with sql_mode=only_full_group_by'

sql_mode 중 only_full_group_by에 맞지 않는 쿼리라고 한다.

💡 sql_mode - only_full_group_by 란?


[공식 문서 정의]
해당 옵션이 활성화되어있을 경우
MySQL은 선택 목록, 조건 또는 목록이 절에 명명되지 않았거나 기능적으로 종속되지 않은 비집계 열을 참조하는 쿼리 를 HAVING거부한다.
즉, mysql이 지정한 group by 표준 규칙에 맞게 쿼리를 수정하라는 뜻이다.

✏️ 에러 해결 방법

해당 에러를 해결하기 위해서는 두가지의 해결 방법이 있다.

1. Query 수정

 SELECT
 	 o.custid
	, c.name
    , MAX(o.payment)
 FROM orders AS o, customers AS c
 WHERE o.custid = c.custid
 GROUP BY o.custid;

위 예시는 GROUP BYo.custid를 그룹화하여 SELECT하는 쿼리이다.
이렇게 작성하면 위의 에러가 나올 것이다.

뭐가 잘못된 걸까?

GROUP BY를 사용할 경우, SELECT 할 수 있는 컬럼은

  1. GROUP BY에 나열된 컬럼 (여기서는 o.custid)
  2. COUNT(), AVG(), SUM()과 같은 집계함수

위의 두가지 경우에만 사용할 수 있다.

그렇다면 다시 위의 쿼리로 돌아갔을 때 잘못된 점이 보일 것이다.

GROUP BY에 선언되지 않은 컬럼, 집계함수도 아닌 컬럼인 c.name으로 인해 문제가 발생한 것이다.

문제를 해결하기 위해서는

해결1. 아래와 같이 비집계 컬럼인 c.name을 GROUP BY에 추가

SELECT
 	 o.custid
	, c.name
    , MAX(o.payment)
 FROM orders AS o, customers AS c
 WHERE o.custid = c.custid
 //GROUP BY o.custid 
 GROUP BY o.custid, c.name

해결2. 어떠한 값이 와도 상관이 없다면 ANY_VALUE() 함수 사용

SELECT
 	 o.custid
	, ANY_VALUE(c.name)
    , MAX(o.payment)
 FROM orders AS o, customers AS c
 WHERE o.custid = c.custid
 GROUP BY o.custid

2. sql_mode 옵션 중 only_full_group_by 비활성화

  • 실제로 해결방법으로 공식문서에도 비활성화하라고 나와있다.
    여기까지 MySQL 공식문서에 나와있는 GROUP BY 규칙이었다.

🧷 참고자료


✍️ 추가 스터디

  • 옵티마이저
  • with & 임시테이블 개념 정리
profile
블로그 이전 중 -> https://devjooj.tistory.com/

0개의 댓글