반응형

sql 실력 향상을 위해 돈을 내고 리트코드를 풀고 있는데, count over partion by 내용이 나와서 올려본다.

 

직원이 속한 팀의 인원수 찾기

-> 유저 Id와 소속 팀 Id가 있는 테이블을 이용해서, 유저별로 속한 팀의 인원수를 알아내기.

키워드 : window function , count over partition by , sql 팀사이즈 찾기

 

 

1303. Find the Team Size

Write an SQL query to find the team size of each of the employees.

Return result table in any order.

The query result format is in the following example.

 

해답

select employee_id, count(employee_id) over (partition by team_id) as team_size
from Employee

어렵게 생각하지 말고 쉽게 생각하면 한줄로 해결되는 문제. Mysql 기반이다. 

반응형
반응형

100줄이 넘는 쿼리를 조금씩 수정해서 덧붙여 나가면서 쓰다보면 쿼리가 왠지 늦어지는 것 같은데..? 하는 생각이 들고 쿼리 튜닝 욕구가 올라온다. 이번에 업무량이 과하지 않아서 조금씩 튜닝을 해 볼 기회가 생겨 틈틈이 공부하는 중인데, 마침 어제 동료 한 분이 요새 공부 잘 되어 가냐고 물어봐서 정리한 것을 공유한다.

 

동료의 한마디..

 


목표 : 인덱스 잘 타네요 라고 말할줄 아는 사람이 된다.

Mysql 튜닝에 관한 내용입니다. (빅쿼리 X . 빅쿼리는 쿼리콘솔에서 따로 확인 가능)

1인덱스 2잘 타네요 에서 인덱스란.

인덱스란 튜닝 용어임.

튜닝할때 오브젝트를 스캔하는 유형은 테이블 스캔과 인덱스 스캔이 있음.

  • 테이블 스캔 : 인덱스를 거치지 않고 바로 디스크에 위치한 테이블 데이터에 접근하는 유형.
  • 인덱스 스캔 : 인덱스로 테이블 데이터를 찾아가는 유형.

스캔 방식

현업에서 주로 사용하는 스캔 방식으로는, 테이블 스캔 유형 중에서는 테이블 풀 스캔 방식, 인덱스 스캔 유형 중에서는 인덱스 범위 스캔, 인덱스 풀 스캔, 인덱스 고유 스캔, 인덱스 루스 스캔, 인덱스 병합 스캔 방식이 있음.

테이블 풀 스캔

인덱스 안 거치고 테이블로 바로 직행해서 처음부터 끝까지 데이터 훑어보는 방식. where절의 조건문을 기준으로 활용할 인덱스가 없다거나, 전체 데이터 대비 대량의 데이터가 필요할때 수행.

성능 면에서 부정적으로 해석됨. 테이블 풀 스캔은 인덱스 없이 사용하는 유일한 방식임.

인덱스 범위 스캔

인덱스를 범위 기준으로 스캔한 뒤 스캔 결과를 토대로 테이블 데이터 찾아감. between~and , < , > , like 구문 등 비교 연산, 비교 구문에 포함될 경우 수행.

좁은 범위를 스캔할때는 성능적으로 매우 효율적이나, 넓은 범위를 스캔할때는 오히려 비효율적임.

인덱스 풀 스캔

인덱스를 처음부터 끝까지 수행. 테이블에 접근하지 않고 인덱스로 구성된 열 정보만 요구하는 Sql문에서 수행.

테이블 풀 스캔 방식보다 성능상 유리하지만, 인덱스라는 오브젝트 전 영역을 모두 검색하는 방식인 만큼 검색 범위를 최대한 줄이는 방향으로 튜닝 필요.

인덱스 고유 스캔

기본키나 고유 인덱스로 테이블에 접근하는 방식. where절에 = 조건으로 작성. 해당 조인 열이 기본키 또는 고유 인덱스의 선두 열로 설정되었을때 활용.

가장 효율적인 스캔 방법

인덱스 루스 스캔

인덱스의 필요한 부분들만 골라 스캔. where + group by , Max() , min() 함수가 포함되면 작동. 이미 오름차순으로 정렬된 인덱스에서 최댓값이나 최솟값이 필요한 경우.

where 절 조건문 기준으로 필요 데이터와 불필요 데이터 구분한뒤 불필요 인덱스 무시.

인덱스 병합 스캔

테이블 내에 생성된 인덱스들을 통합(결합, 교차)해서 스캔하는 방식. WHERE 문 조건절들의 열들이 서로 다른 인덱스로 존재하면 옵티마이저가 해당 인덱스를 가져와서 모두 활용.

물리적으로 존재하는 개별 인덱스를 각각 수행하므로 인덱스 접근 시간이 배로 걸림. 따라서 별개로 생성된 인덱스를 보통 하나의 인덱스로 통합 하거나 sql 문자체를 독립된 하나의 인덱스만 수행하도록 변경하는 튜닝을 진행.

 

 

(출처 : 업무에 바로 쓰는 SQL 튜닝)

 


여담

사실 찐 시작은 이러했음 

이후에 세현님은 아직 공부 시작을 못 하신듯 하고, 나도 따로 스터디를 진행해보지는 못했지만.. 공부는 하고 있다 이거야!

반응형

'sql' 카테고리의 다른 글

sql 윈도우 함수 count over partition by (Feat.리트코드)  (0) 2023.01.25
sql join 예제(조건에 따른 join)  (0) 2023.01.17
mysql 누적집계 sum over  (0) 2022.12.26
반응형

Join은 sql의 시작과 끝

실무를 하다보면 결국 join (inner join)과 left join 을 가장 많이 쓰게 되는데 실제 데이터와 쿼리를 보면서 어떤식으로 결과가 나오는지 확인해본다. 

데이터는 Join을 이해하기 쉽도록 포인트 적립과 사용에 대한 데이터를 예제로 사용해본다.

 

데이터 준비 

- write (DDL) 쿼리가 안되는 환경을 고려해서 다음과 같이 데이터를 넣는다. 

상황 : AA, BB 두명의 유저가 있고 포인트를 쌓거나 사용하고 있으며 각각 get_point, use_point에 로그를 남긴다. . 

point 테이블 : 간단하게 idx(index, Primary Key값, 고유번호를 의미하는 컬럼)만 넣은 테이블

get_point 테이블 : 포인트 적립 (p_idx, idx, uid, point) , p_idx로 point.idx와 엮을 수 있다.

use_point 테이블 : 포인트 사용 (p_idx, idx, uid, point) , p_idx로 point.idx와 엮을 수 있다.

*원래 idx , p_idx , uid, point 순서가 더 자연스러운데 중간에 수정하다보니.. 글을 전체 수정하기 어려워서 이대로 감.

with point as (
select     1  IDX union all
select     2  union all
select     3  union all
select     4  union all
select     5  union all
select     6  union all
select     7  union all
select     8  union all
select     9  union all
select     10  union all
select     11  union all
select     12  union all
select     13  union all
select     14  union all
select     15  union all
select     16 
)

,get_point as (
select    1 p_IDX, 1  IDX,    'AA'  UID,   1000 POINT union all
select    2 ,    2    ,     'AA'   ,      1500 union all
select    3 ,    3    ,     'BB'   ,      1000 union all
select    4 ,    4    ,     'AA'   ,      1000 union all
select    6,     5    ,     'BB'   ,      2000 union all
select    7,     6    ,     'AA'   ,      500 union all
select    10,    7    ,     'BB'   ,      5000 union all
select    11,    8    ,     'AA'   ,      10000 union all
select    15,    9    ,     'BB'   ,      3000 union all
select    16,   10    ,     'AA'   ,      5000 
)

,use_point as (
select   5 p_IDX,  1  IDX,    'AA'  UID,   -1500 POINT union all
select   8,      2    ,       'AA'   ,     -200 union all
select   9,      3    ,       'BB'   ,      -100 union all
select   12,     4    ,     'AA'   ,      -100 union all
select   13,     5    ,     'BB'   ,      -2800 union all
select   14,     6    ,     'AA'   ,      -500 
)

 

 

데이터 확인

  • point 테이블
select * from point p

 

  •  get_point 테이블
select * from get_point gp

 

 

  • use_point 테이블
select * from use_point up

 

 

inner join 결과

이때, 만약 전체를 join 한다면?

select * from point p 
join get_point gp on gp.p_idx = p.idx
join use_point up on up.p_idx = p.idx

=> 아무것도 나오지 않는다.

 

 

호옥..시 point.idx별로 join 이 되어서 전체 적립/사용 테이블이 나오길 기대했나? 

아니다. 그걸 보기 위해서는 Left join 으로 처리해야한다. 

 

 

left join 결과

select * from point p
Left join get_point gp on p.idx=gp.p_idx 
left join use_point up on p.idx=up.p_idx

이렇게 했더니 원하는 것을 볼 수 있게 되었다. 

 

case when 사용해서 정리하기

위 쿼리의 결과로 나온 테이블은 뭔가 정리가 안 되어 있음. 원하는것에 아직 한발 부족함. 

그럴때는 case when을 사용함. 

먼저 위의 Left join 두개 사용한 쿼리를 With 문으로 서브쿼리로 만들어 주고, 해당 테이블을 다시 사용하면서 case 문을 이용하는 방식

,tbl as (
select p.idx, 
      gp.idx gp_idx, gp.p_idx gp_p_idx,  gp.uid gp_uid, gp.point gp_point, 
      up.idx up_idx, up.p_idx up_p_idx,  up.uid up_uid, up.point up_point
from point p 
left join get_point gp on p.idx=gp.p_idx 
left join use_point up on p.idx=up.p_idx 
)


select tbl.idx,
      case when gp_idx is not null then gp_idx else up_idx end gu_idx, 
      case when gp_idx is not null then gp_point else up_point end point,
      case when gp_idx is not null then gp_uid else up_uid end uid
 from tbl

그럼 요렇게 나온다. 

반응형
반응형

종종 누적집계 함수가 헷갈린다..
- 빅쿼리에도 쓸수 있음. mysql 버전으로 작성

- sum over
예제

- gender, day가 나오면서 score_points 를 누적 집계 하려면

# Write your MySQL query statement below
select gender, day, sum(score_points) over( partition by gender order by gender, day) as total 
from Scores group by 1,2 order by 1,2

- 누적집계할 컬럼을 sum() 안에 넣어준다 => sum(score_points)
- over() 안에 값을 입력
-크게는 gender 별로 누적집계를 보고 싶은 것이니 partition by gender (누적되어 나오는 마지막 값은 성별 누적값을 보고 싶다는 뜻.)
-누적 집계는 순서가 중요함. order by 다음에 gender, day 로 날짜별로 누적되어 나오게 설정.

결과


꿀팁
Sum over 쓸때 partition by 를 안해도 되고 (이때는 전체 테이블이 Order by 기준으로만 누적집계 되어 나옴)
order by 가 안들어가도 됨 (거의 쓸일 없을듯? 뭐.. 한달 급여 테이블이 있고 사원이 있고 지불된 총 한달 급여를 보고 싶으면 쓰겠지만 웬만해서는 Order by 가 필요할 것 같음.)

반응형

+ Recent posts