ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Real MySQL [7-15] 쿼리 작성 및 최적화 - GROUP BY
    MySQL 2017. 3. 10. 21:28



    본 게시물의 내용과 이미지는 도서 Real MySQL의 내용을 재구성하여 작성되었습니다. 저자, 출판사에 의해 저작권 문제 발생시 게시물이 비공개 될 수 있음을 알립니다. 



    GROUP BY 사용 시 주의사항


    쿼리에 GROUP BY가 사용되면 그룹 키(GROUP BY 절에 명시된 칼럼)가 아닌 칼럼은 일반적으로 집합 함수를 감싸서 사용해야 한다. 오라클과 같은 DBMS 에서는 이 규칙을 지키지 않으면 에러가 난다. 하지만 MySQL에서는 그룹 키가 아닌 칼럼이더라도 쿼리에서 집합 함수 없이 그냥 사용할 수 있다. 


    SELECT first_name FROM employees GROUP BY gender;


    SELECT first_name, last_name, COUNT(*)

    FROM  employees

    GROUP BY first_name ORDER BY  last_name;


    첫번재 쿼리는 gender 로 GROUP BY를 수행했기 때문에 결과 레코드는 2건(남자, 여자)이 반환될 것이다. 하지만 SELECT 하는 컬럼은 first_name 을 조회하고 있다. 반환되는 first_name 은 남녀 성별로 한 건씩만 가져오긴 할 것이다. 하지만 가져온 first_name 이 제일 큰 값인지, 제일 작은 값인지 아니면 중간의 값을 가져온 것인지 알 수 없다.


    두번째 쿼리 또한 first_name 칼럼으로 GROUP BY를 수행해서 결과의 last_name 으로 정렬을 수행하고 있다. 이 결과 또한 first_name 이 동일한 여러 사원중 어느 사원의 last_name 을 가지고 정렬을 수행했는지 보장할 수 없는 쿼리다. 그래서 이 쿼리는 실제 조회된 결괄르 살펴보면 last_name 칼럼의 값이 정렬되지 않은 채로 출력될 때도 있다. 


    이러한 GROUP BY 사용은 쿼리 가독성을  떨어뜨린다. 쿼리 작성자로부터 설명을 듣지 않으면 무엇을 조회하고자 했는지 파악할 방법이 없다. 가능하다면 GROUP BY 절에 명시되지 않은 칼럼은 반드시 집합 함수로 감싸서 사용하길 권장한다.


    GROUP BY 에 명시되지 않은 칼럼은 집합 함수로 감싸서만 사용할 수 있게 하는 것을 FULL GROUP-BY 라고 한다. sql_mode 시스템 변수는 MySQL 모드를 설정할 수 있는데  ONLY_FULL_GROUP_BY 값을 설정하면 FULL GROUP-BY만 사용할 수 있다. 




    GROUP BY .. ORDER BY NULL


     MySQL의 GROUP BY은 그룹핑과 정렬 작업을 동시에 수행한다. 실제 정렬 작업이 필요하지 않을 때는 정렬은 하지 않도록 쿼리를 작성할 수 있다.


    MYSQL에서 GROUP BY 가 불필요한 정렬 작업을 하지 않게 하려면 GROUP BY 를 수행할 때 ORDER BY NULL 이라는 키워드를 사용해야 한다. GROUP BY 결과 건수가 많아지면 많아질수록 정렬 작업으로 인한 성능 저하가 커진다. 


    EXPLAIN

    SELECT from_date

    FROM salaries

    GROUP BY from_date;


    위 쿼리는 GROUP BY 절만 사용했지만 실제 실행 계획상에 Using Filesort 까지 표시된 것으로 보아 별도의 정렬 작업이 수행됬음을 알 수 있다.




    EXPLAIN

    SELECT from_date
    FROM salaries

    GROUP BY from_date ORDER BY NULL;


    위 쿼리의 실행계획에서는 Using Filesort 가 사라졌다. 필요한 그룹핑 작업만 처리하고 그 결과를 별도의 정렬 작업 없이 반환한 것이다.






    GROUP BY col1 ASC col2 DESC


    거의 사용하지는 않지만 MySQL의 GROUP BY 절 칼럼에 정렬 순서를 명시할 수 있다. MySQL의 GROUP BY 가 정렬까지 수행하기 때문에 이런 문법이 가능하다. 명시되는 칼럼에 오름차순 또는 내림차순 키워드를 명시할 수 있다.


    SELECT titl, from_date

    FROM titles

    GROUP BY  title DESC, from_date ASC;


    위 쿼리를 실행하면 title 칼럼이 역순으로 정렬되어 출력된다 



    주의해야 할 점은 GROUP BY 절의 칼럼에 명시하는 정렬 순서가 혼용되면 인덱스를 사용할 수 없게된다. GROUP BY 결과가 정렬되어야하는 경우 별도의 ORDER BY절을 사용해야 쿼리의 의미가 명확하게 전달될 수 잇따. 




    GROUP BY .. WITH ROLLUP


    GROUP BY 결과의 그룹별로 소계를 가져오는 ROLL UP 기능을 사용할수 있다. ROLLUP으로 출력되는 소계는 단순히 최종 합만 가져오지 않고 GROUP BY 에 사용된 칼럼의 개수에 따라 소계의 레벨이 달라진다. 


    다음 쿼리는 dept_emp 테이블을 부서 번호로 그룹핑하는 예제이다


    SELECT dept_no, COUNT(*) FROM dept_emp

    GROUP BY dept_no

        WITH ROLLUP;


    WITH ROLLUP 과 함께 사용된 GROUP BY 쿼리 결과는 그룹별 소계를 출력하는 레코드가 추가되어 표시된다. 소계 레코드의 칼럼값은 항상 NULL로 표시된다는 점에 주의해야 한다. 



    GROUP BY 칼럼이 2개인 다음 쿼리를 한번보자, 사원의 first_name 과 last_name 으로 그룹핑하는 예제이다.


    SELECT first_name, last_name, COUNT(*)

    FROM employees

    GROUP BY first_name, last_name

        WITH ROLLUP;


    GROUP BY 칼럼이 2개로 늘어나면서 소계가 2단계로 표시되었다. first_name 그룹별 소계 레코드가 출력되고 제일 마지막에 전체 총계가 출력된다. 소계나 총계 레코드는 항상 해당 그룹의 마지막에 나타난다. 




    안타깝게도 GROUP BY .. ROLLUP 기능은 ORDER BY 와 함께 사용할 수 없다. ROLLUP 기능이 LIMIT 와 함께 사용되는 경우 결과가 혼란스러울 수 있다. GROUP BY .. WITH ROLLUP 이 처리된 이후 LIMIT 가 수행되므로 LIMIT 페이징 처리를 하는 것은 주의해야 한다. 



Designed by Tistory.