home
  
  
  
  
    
효율적으로 
  
    
 
      
    Group by deep dive
- 24년 6월 24일에 열린 AUSG 빅챗 발표 중 이태현님의 발표를 듣고, 더 찾아보며 정리한 내용입니다.
GROUP BY은 어떤 조건에서 실행되는가
- GROUP BY절은 특정 열을 기준으로 데이터를 그룹화하여 집계 함수(예:- MAX,- SUM,- COUNT,- AVG)를 사용할 때 유용
- 하지만 MySQL에서는 sql_mode설정에 따라GROUP BY절의 동작 방식이 달라질 수 있다.
예시 쿼리
- artwork-view-log테이블에서는 artwork와, 그것을 본 user_id 그리고 본 시각이 적혀있는 log가 존재한다.
- artwork-view-log테이블에서 사용자별로 가장 마지막에 본 작품을 찾는 쿼리는 다음과 같다
SELECT 
    user_id,
    MAX(view_datetime) AS last_view_datetime,
    artwork_id
FROM 
    artwork_view_log
GROUP BY 
    user_id;
- 하지만 위 쿼리는 실행되지 않는다.  이유는 artwork_id가GROUP BY절에 포함되지 않았고, 집계 함수로 처리되지 않은 컬럼이기 때문이다.
- MySQL의 sql_mode설정에 따라 이런 경우 오류가 발생할 수 있다.
sql_mode = only_full_group_by
- sql_mode에- only_full_group_by가 설정되어 있는 경우,- GROUP BY에 포함되지 않은 필드를- SELECT절에 포함하면 오류가 발생
- 하지만 함수적 종속성을 고려하면 only_full_group_by설정을 바꾸지 않아도 그룹바이가 실행된다.
- RDS, AURORA에는 ONLY_FULL_GROUP_BY를 직접적으로 바꿀 수 없다.
함수적 종속성 (Functional Dependency)
- 함수적 종속성은 데이터베이스 이론에서 특정 속성 집합이 다른 속성 집합을 유일하게 식별할 수 있는 관계를 의미한다.
    - 예를 들어, 테이블에서 A -> B가 성립하면,A값이 주어졌을 때B값이 유일하게 결정된다는 뜻
 
- 예를 들어, 테이블에서 
- 데이터 무결성과 관련이 깊음
| user_id | user_email | user_type | 
|---|---|---|
| 1 | user1@example.com | admin | 
| 2 | user2@example.com | user | 
| 3 | user3@example.com | user | 
- user_id는 기본 키이며,- user_email과- user_type은- user_id에 함수적으로 종속
- 즉, user_id가 주어지면user_email과user_type은 유일하게 식별 가능하다.
- 만약에 집계되지 않음 컬럼이 select되더라도, Group By 구의 대상이 되는 컬럼과 함수적 종속성이 있다면 집계가 가능하다.
함수적 종속성 예시
SELECT 
    user_id,
    MAX(user_email) AS max_email,
    user_type
FROM 
    users
GROUP BY 
    user_id;
- user_type필드가- GROUP BY절에 포함되지 않았고, 집계 함수로 처리되지 않았기 때문에 원래대로라면 쿼리는 위의 논리대로라면 실행되면 안된다.
- 그러나 user_id가 기본 키이므로,user_id가 주어지면user_email과user_type이 유일하게 식별된다. 따라서 쿼리 실행이 가능하다.
요약
- 함수적 종속성: 특정 속성 집합이 다른 속성 집합을 유일하게 결정할 수 있는 관계.
- 함수적 종속성이 있다면GROUP BY절에 집계되지 않은 필드를SELECT절에 포함할 수 있음.
- sql_mode설정:- ONLY_FULL_GROUP_BY설정이 활성화된 경우,- GROUP BY절에 포함되지 않은 필드를- SELECT절에 포함할 수 없음.
효율적으로 GROUP BY 사용하기
복합 인덱스와 GROUP BY 필드 순서 맞추기
- 인덱스 구성: UNIQUE(user_id, artwork_id, view_datetime)와 같은 복합 인덱스를 사용하면 쿼리 성능이 향상
- 정렬 순서: GROUP BY절의 필드 순서가 인덱스의 필드 순서와 일치하면 인덱스를 효과적으로 사용 가능
- EXPLAIN: 쿼리 실행 계획을 확인하여 인덱스가 제대로 사용되는지 확인 가능
    - 순서를 맞추지 않으면 Using temporary발생
- 인덱스 정렬 순서가 탐색에 반영되지 못해서, 임시 테이블을 만들고 거기서 임시로 정렬을 한다. -> 비효율적
- 반면에 순서를 맞추면 index full search가 일어남. -> 적용이 됨
 
- 순서를 맞추지 않으면 
- 이미 충분히 효율적이지만 COUNT(DISTINCT)를 통해서 더 최적화 가능
DISTINCT를 사용하여 범위 검색
SELECT 
    user_id, 
    artwork_id, 
    COUNT(DISTINCT view_datetime)
FROM 
    views
GROUP BY 
    user_id, artwork_id;
- 각 (user_id, artwork_id) 조합에 대해 고유한 view_datetime 값의 개수를 세는 쿼리이다.
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | 
|---|---|---|---|---|---|---|---|---|---|
| 1 | SIMPLE | views | range | PRIMARY | PRIMARY | 12 | NULL | 1000 | Using index for group-by | 
- explain을 실행하면 다음과 같이 나오는데, type: range에 주목하자
- 만약 DISTINCT 키워드가 사용된 쿼리에서 해당 열에 인덱스가 존재한다면, 데이터베이스는 이 인덱스를 사용하여 효율적으로 범위 탐색 가능
- 인덱스를 사용하여 중복된 값을 건너뛸 수 있는데 이러한 범위 탐색은 인덱스가 정렬된 상태로 저장되어 있기 때문에 가능