인프런 커뮤니티 질문&답변

레스님의 프로필 이미지
레스

작성한 질문수

BigQuery(SQL) 활용편(퍼널 분석, 리텐션 분석)

[인프런 빅쿼리 빠짝스터디 2주차] 윈도우함수 활용 방법과 null을 다루는 법

작성

·

43

0

[1~2번]윈도우 함수(탐색 함수) 연습 문제

 

1번

select
  *,
  lead(visit_month, 1) over(partition by user_id order by visit_month) as next_visit_month,
  lead(visit_month, 2) over(partition by user_id order by visit_month) as two_next_visit_month
from `advanced.analytics_function_01

 

2번

select
  *,
  lead(visit_month, 1) over(partition by user_id order by visit_month) as next_visit_month,
  lead(visit_month, 2) over(partition by user_id order by visit_month) as two_next_visit_month,
  lag(visit_month, 1) over(partition by user_id order by visit_month) as before_visit_month
from `advanced.analytics_function_01`

 

 

[1~4번] 윈도우 함수 Frame 연습 문제

 

1번

/*
전체 계산 할 때 왜 over()를 써야 하는지 몰랐습니다.

그러나 일반 집계 함수의 방식과 비교해보니, 사용 이유를 알게 됐습니다.

집계함수를 사용할 때 over()를 사용했다는 건 '윈도우 함수가 적용된 결과값(각 행마다 결과 값이 반환 되는 것)으로 보겠다'는 의미인 걸 알게 됐습니다.

같은 집계함수를 사용하더라도 '어떤 방식(결과를 1개 행으로만 받아볼지, 여러개 값으로 받아볼지)으로 결과 값을 받아 볼 지' 생각해 봐야 한다는 걸 알았습니다.
*/

select
  *,
  sum(amount) over() as amount_total
from `advanced.orders`

 

2번

/*
처음에는 누적합을 구하는 건데 왜 over() 안에 order by를 써야하는지 이해하지 못했습니다.

그러나 누적합도 "어떤 방식으로 계산할지 '계산 방식'"을 지정해 줘야 한다는 걸 알게 됐습니다.
/*

select
  *,
  sum(amount) over(order by order_id) as cumulative_sum
from `advanced.orders`

 

3번

select
  *,
  sum(amount) over(partition by user_id order by order_id) as cumulative_sum_by_user 
from `advanced.orders`

 

4번

/*
over() 안에 사용하는 order by와 over() 밖에서 사용하는 order by를 명확히 이해했습니다.

윈도우 함수에서 사용하는 order by는 '계산 순서'를 정하는 것이고, 윈도우 함수 밖에서 사용한 order by는 '눈에 보이는 정렬'을 컨트롤 한다는 걸 이해했습니다.

이해할 때 '쿼리 작동 순서'를 생각했더니, 큰 도움이 됐습니다.
*/

select
  *,
  avg(amount) over(order by order_id rows between 5 preceding and 1 preceding) as last_5_orders_avg_amount
from `advanced.orders`
order by
  order_id

 

 

[1~7번] 윈도우 함수 연습 문제

 

1번

select
  *,
  count(user) over(partition by user) as total_query_cnt
from `advanced.query_logs`
order by
  query_date

 

2번

/*
- date 형식 데이터를 extract()로 주 형식으로 바꾸는 걸 알게 됐습니다
- partition by 내에 2개 이상 컬럼을 넣어도 된다는 걸 알게 됐습니다.

*/
with user_query_cnt as (
  select
  extract(week from query_date) as week_number,
  team,
  user,
  count(user) as query_cnt
from `advanced.query_logs`
group by all
)

select
  *,
  rank() over(partition by week_number, team order by query_cnt desc) as team_rank
from user_query_cnt
qualify team_rank = 1
order by
  1,2

 

3번

/*
- lag()와 함께 쓰는 over() 사용법이 헷갈렸는데, 실행 후 수정하는 방향으로 쿼리를 작성해 보라고 하신 점이 인상 깊게 와닿았습니다.
*/
with base as (
select
  user,
  team,
  extract(week FROM query_date) as week_number,
  count(user) as query_count
from advanced.query_logs
group by all
)

select
  *,
  lag(query_count, 1) over(partition by user order by week_number) as pre_query_cnt
from base


4번

/*
qualify cumulative_query_cnt != cumulative_query_cnt2

위 쿼리로 정합성을 체크하는 방식을 배우게 됐습니다.
*/
## 4번
with query_cnt as (
select
  *,
  count(user) as query_count
from advanced.query_logs
group by all
)

select
  *,
  sum(query_count) over(partition by user order by query_date) as cumulative_query_cnt,
  sum(query_count) over(partition by user order by query_date rows between unbounded preceding and current row) as cumulative_query_cnt2
from query_cnt
-- qualify cumulative_query_cnt != cumulative_query_cnt2

 

5번

/*
- 4번에서 배운 정합성 체크하는 쿼리를 이용해서 컬럼 내 값이 있는지 테스트 해봤습니다.
*/

## 정합성 테스트
-- select
--   *
-- from (
-- select
--   *,
--   last_value(number_of_orders) over(order by date) as last_values_order
-- from raw_data
-- ) as test1
-- where
--   number_of_orders != last_values_order


select
  *,
  last_value(number_of_orders ignore nulls) over(order by date) as last_values_order
from raw_data

 

6번

/*
처음에는 1. 평균을 구하고 2. 해당 테이블을 서브쿼리로 두고, lag(컬럼, 2)를 해야 하는줄 알았습니다.

그러나 Frame 사용 방법이 생각났고, 조건에 맞게 범위를 설정해서 문제를 해결했습니다.
*/

## 6번

, base2 as (
select
  *,
  last_value(number_of_orders ignore nulls) over(order by date) as last_values_order,
from raw_data
)

select
  *,
  avg(last_values_order) over(order by date rows between 2 preceding and current row) as moving_avg
from base2

 

7번

/*
- 타임스탬프 데이터를 다루는 다양한 방법을 알게 됐습니다.
- 실무에서는 세션 기준과 보고 싶은 유저 혹은 이벤트를 지정해서 사용해도 된다는 걸 알게 됐습니다.
*/


with base as (
select
  event_date,
  event_timestamp,
  datetime(timestamp_micros(event_timestamp), 'Asia/Seoul') as event_datetime,
  event_name,
  user_id,
  user_pseudo_id
from `advanced.app_logs`
where 
  event_date = '2022-08-18'
  and user_pseudo_id = '1997494153.8491999091'
)


select
  *,
  sum(session_start) over(order by event_datetime) as session_id
from (
select
  *,
  case
    when second_diff >= 20 then 1
    when second_diff is null then 1
    else 0
  end session_start
from (
select
  *,
  timestamp_diff(event_datetime, before_event_datetime, second) as second_diff
from (
select
  *,
  lag(event_datetime, 1) over(partition by user_pseudo_id order by event_datetime) as before_event_datetime
from base
) as base2
) as base3
) as base4

 

답변 1

0

안녕하세요, 인프런 AI 인턴입니다. 레스님.

윈도우 함수와 null을 다루는 방법에 대한 질문에 감사를 드립니다. 윈도우 함수는 데이터를 다양한 방식으로 분석하는데 강력한 도구이며, null 처리는 데이터 정확성을 유지하는 데 중요한 역할을 합니다.

이 질문을 통해 알아봐야 할 몇 가지 핵심 개념과 일반적인 사용 방법을 설명하겠습니다.

  1. 윈도우 함수의 사용 이유:

    • OVER() 구문은 데이터의 특정 파티션에서 일련의 행에 대한 계산을 수행할 때 사용됩니다. 이러한 함수는 기본적인 집계 함수와는 다르게 각 행에 대해 반복적으로 계산을 수행하여 결과를 제공합니다. 예를 들어, SUM() 함수로 특정 컬럼의 총합을 구할 때 OVER()를 사용하면 각 행에 대해 누적 합계를 계산할 수 있습니다.
  2. NULL 값 처리:

    • SQL에서 NULL 값은 부재를 의미하므로 집계 작업에서 특별한 고려가 필요합니다. 예를 들어 LAST_VALUE() 함수 사용 시 IGNORE NULLS 옵션을 통해 NULL 값을 무시하고 계산할 수 있습니다.
  3. 윈도우 함수와 프레임:

    • 프레임 사양은 윈도우 함수의 범위를 정의합니다. 예를 들어, ROWS BETWEEN 5 PRECEDING AND 1 PRECEDING 지정은 현재 행 전의 5개 행부터 1개 행까지 고려하여 계산을 수행하겠다는 의미입니다.

질문에 대한 유사한 기존 답변을 확인하고 싶다면, 아래 링크들을 참고해 보시면 좋겠습니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다. 현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏 추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

레스님의 프로필 이미지
레스

작성한 질문수

질문하기