SparkSql 에서 시간 한도 조작 [창 함수]
구조 데이터 테스트
잘 자 르 기 위해 저 는
@
로 맞 췄 습 니 다. 첫 번 째 는 날짜 이 고 두 번 째 는 사용자 이 며 세 번 째 는 소비 여부 이 며 네 번 째 는 소비 금액 입 니 다.20190531@156@1@20
20190601@156@1@20
20190602@156@1@10
20190603@156@0@0
20190604@156@0@0
20190605@156@1@10
20190606@156@1@10
20190607@156@1@10
20190608@156@0@0
20190609@156@1@20
20190610@156@1@20
20190531@187@0@0
20190601@187@1@10
20190602@187@1@20
20190603@187@1@30
20190604@187@1@40
20190605@187@0@0
20190606@187@1@10
20190607@187@0@0
20190608@187@1@20
20190609@187@1@20
20190610@187@1@10
20190609@173@0@0
20190610@173@1@10
다음 구조
create table tmp_time_exp
(
dt string,
passenger_phone string,
is_call string comment ' ',
cost bigint comment ' '
)
row format DELIMITED fields terminated by '@'
STORED AS INPUTFORMAT
'org.apache.hadoop.mapred.TextInputFormat'
OUTPUTFORMAT
'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
location '/hdfslocation'
일치 하 는 지 확인 해 보 세 요.
tmp_time_exp.dt tmp_time_exp.passenger_phone tmp_time_exp.is_call tmp_time_exp.cost
20190531 156 1 20
20190601 156 1 20
20190602 156 1 10
20190603 156 0 0
20190604 156 0 0
20190605 156 1 10
20190606 156 1 10
20190607 156 1 10
20190608 156 0 0
20190609 156 1 20
20190610 156 1 20
20190531 187 0 0
20190601 187 1 10
20190602 187 1 20
20190603 187 1 30
20190604 187 1 40
20190605 187 0 0
20190606 187 1 10
20190607 187 0 0
20190608 187 1 20
20190609 187 1 20
20190610 187 1 10
20190609 173 0 0
20190610 173 1 10
흔 한 문제
1. n 일 연속 소비 유저 구하 기
예: 3 일 연속 소비 하 는 사용 자 를 찾 아야 한다 면 그의 연속 소비 시작 시간 과 종료 시간
select
passenger_phone,
is_call,
cost,
unix_timestamp(lag(dt,2,0) over(partition by passenger_phone order by dt),'yyyyMMdd') as start_dt,
dt as end_dt,
datediff(from_unixtime(unix_timestamp(dt,'yyyyMMdd'),'yyyy-MM-dd'),from_unixtime(unix_timestamp(lag(dt,2,0) over(partition by passenger_phone order by dt),'yyyyMMdd'),'yyyy-MM-dd')) as last3day
from
tmp_time_exp
where
is_call != 0
having
last3day = 2
결과 출력
passenger_phone is_call cost start_dt end_dt last3day
156 1 10 1559232000 20190602 2
156 1 10 1559664000 20190607 2
187 1 30 1559318400 20190603 2
187 1 40 1559404800 20190604 2
187 1 10 1559923200 20190610 2
1.
datediff
을 사용 할 때 전달 하 는 매개 변 수 는 반드시 표준 날짜 형식 이 어야 하기 때문에 전환 해 야 합 니 다.2. lag
또는 lead
를 사용 하면 유사 한 조작 을 할 수 있다. 먼저 사용 자 를 그룹 으로 나 눈 다음 에 소비 시간 을 정렬 한 다음 에 다음 소비 시간 을 이동 시 킨 다음 에 차 이 를 낸다.위 와 같이 연속 날 짜 를 두 위치 로 옮 기 고 2 로 줄 이면 3 일 동안 계속 로그 인해 야 한 다 는 것 을 잘 이해 할 수 있 습 니 다.2. 사용자 가 연속적으로 소비 하 는 시간 대, 지속 시간 및 해당 시간 대의 소비 금액 총계
예 를 들 어 156 명의 사용자 가 연속 으로 소비 하 는 시간 대 는 5.31 - 6.2 이다.6.5-6.7;6.9 - 6.10, 금액 은 각각 50, 30, 40 이다.
select
passenger_phone,
min(dt) as start_day,
max(dt) as end_day,
count(1) as last_days,
sum(cost) as cost_sum
from
(
select
*,
row_number() over(partition by passenger_phone order by dt) as ranker
from
tmp_time_exp
where
is_call != 0
)a
group by
passenger_phone,date_sub(from_unixtime(unix_timestamp(dt,'yyyyMMdd'),'yyyy-MM-dd'),ranker)
출력 결과
passenger_phone start_day end_day last_days cost_sum
156 20190531 20190602 3 50
156 20190605 20190607 3 30
156 20190609 20190610 2 40
173 20190610 20190610 1 10
187 20190601 20190604 4 100
187 20190606 20190606 1 10
187 20190608 20190610 3 50
상기 처리 방식 도 블 로그 의 처 리 를 참고 하여 링크 를 찾 을 수 없습니다. 교묘 하 게 처리 되 었 습 니 다. 날짜 정렬 방식 으로 자신의 날짜 와 차 이 를 나 누 어 조 를 나 누 었 습 니 다. 차이 가 모두 같다 면 연속 적 인 날짜 임 을 설명 하고 이 차이 가 같은 개 수 는 연속 적 인 일수 입 니 다.
3. 6.10, 연속 소비 일 수 를 포함 하여 절대 계산 하지 않 음 (소비 출석 일수)
예: 156 의 사용자.6.10 소 비 했 고 앞으로 밀 었 고 6.9 도 소 비 했 지만 6.8 은 소 비 를 하지 않 았 기 때문에 지금까지 계속 소비 한 시간 은 이틀 이다.이것 은 출석 과 유사 한 기능 에 많이 사용 되 는데, 만약 오늘 서명 을 끊 으 면, 누 적 된 출석 일 수 를 다시 계산 하기 시작한다
방법
select
*
from
(
select
passenger_phone,
min(dt) as start_time,
max(dt) as end_time,
count(1) as day_cnt
from
(
select
*,
row_number() over(partition by passenger_phone order by dt) as ranker
from
tmp_time_exp
where
is_call = 1
)aa
group by
passenger_phone,date_sub(from_unixtime(unix_timestamp(dt,'yyyyMMdd'),'yyyy-MM-dd'),ranker)
)bb
where
end_time = '20190610'
문제 2 에 서 는 종료 일 자 를 오늘 (6.10) 로 직접 한정 하면 나 올 수 있다.
방법
with end_dt as
(
select
passenger_phone,
max(dt) as end_dt
from
tmp_time_exp
where
dt between '20190531' and '20190610'
and is_call = 0 --
group by
passenger_phone
)
select
aa.dt,
aa.passenger_phone,
datediff(from_unixtime(unix_timestamp(aa.dt,'yyyyMMdd'),'yyyy-MM-dd'),from_unixtime(unix_timestamp(bb.end_dt,'yyyyMMdd'),'yyyy-MM-dd')) as day_cnt
from
(
select
dt,
passenger_phone
from
tmp_time_exp
where
dt = '20190610' --
)aa
join
end_dt as bb
on
aa.passenger_phone = bb.passenger_phone
각 사용자 의 가장 큰 소비 하지 않 는 날 짜 를 먼저 가 져 옵 니 다. 6.10 부터 앞으로 밀어 서 첫 번 째 소비 하지 않 는 날 짜 를 만 날 때 까지 멈 출 수 있 기 때 문 입 니 다. 그러면 6.10 까지 소비 가 끊 이지 않 는 시간 을 얻 을 수 있 습 니 다.
결국
passenger_phone start_time end_time day_cnt
156 20190609 20190610 2
173 20190610 20190610 1
187 20190608 20190610 3
4. 최 장 연속 소비 일수
예 를 들 어 156 명의 사용자 가 연속 으로 소비 하 는 시간 대 는 5.31 - 6.2 이다.6.5-6.7;6.9 - 6.10, 시간 은 각각 3, 3, 2 이다.금액 은 각각 50, 30, 40 이 문제 2 의 파생 이다.
방법
select
passenger_phone,
start_day,
end_day,
last_days,
rank() over(partition by passenger_phone order by last_days desc) as appose_rank, --
row_number() over(partition by passenger_phone order by last_days desc) as last_ranker --
from
(
select
passenger_phone,
min(dt) as start_day,
max(dt) as end_day,
count(1) as last_days
from
(
select
*,
row_number() over(partition by passenger_phone order by dt) as ranker
from
tmp_time_exp
where
is_call != 0
)a
group by
passenger_phone,date_sub(from_unixtime(unix_timestamp(dt,'yyyyMMdd'),'yyyy-MM-dd'),ranker)
)aa
having
-- last_ranker = 1
appose_rank = 1
문제 2 의 해법 을 사용 하여 그 결 과 를 직접 다음 층 으로 계산 하면 된다. 즉, 가장 긴 소비 시간 을 직접 꺼 내 는 것 이다.
방법
select
cc.*,
length(dd) as max_length,
row_number() over(partition by passenger_phone order by length(dd) desc) as ranker
from
(
select
passenger_phone,
concat_ws('',collect(is_call)) as call_list
from
(
select
dt,
passenger_phone,
is_call
from
tmp_time_exp
order by
passenger_phone desc, dt desc
)aa
group by
passenger_phone
)cc
lateral view explode(split(call_list,'0')) asTable as dd
having
ranker = 1
비교적 교묘 한 방식 은 한 번 의 면접 과정 에서 면접 관 이 저 에 게 알려 준 해법 입 니 다. 똑 같이 이 문 제 를 해결 할 수 있 지만 날 짜 를 더 해 야 한다 면 조금 더 복잡 할 것 입 니 다. 전기 concat 일부 날짜 의 데 이 터 를 필요 로 한 다음 에 후기 에 풀 어야 합 니 다.
결 과 는 모두 일치한다.
passenger_phone start_day end_day last_days appose_rank last_ranker
156 20190531 20190602 3 1 1
156 20190605 20190607 3 1 2
173 20190610 20190610 1 1 1
187 20190601 20190604 4 1 1
5. 소비 피크 날짜
예: 당일 소비자 수가 가장 많은 날짜
방법
select
dt,
passenger_phone,
is_call_cnt,
rank() over(order by is_call_cnt desc) as call_ord_ranker
from
(
select
*,
sum(is_call) over(partition by dt) as is_call_cnt
from
tmp_time_exp
)aa
having
call_ord_ranker = 1
방법
select
*,
first_value(dt) over(order by is_call_cnt desc) as max_dt
from
(
select
*,
sum(is_call) over(partition by dt) as is_call_cnt
from
tmp_time_exp
)aa
having
max_dt = dt
결실
dt passenger_phone is_call cost is_call_cnt max_dt
20190610 187 1 10 3.0 20190610
20190610 173 1 10 3.0 20190610
20190610 156 1 20 3.0 20190610
6. 소비 누적 x 원 도착 날짜
예 를 들 어 156 명의 사용자 가 소비 가 50 위안 에 처음 도착 한 날 짜 는 6.2 호 이 고 100 위안 에 처음 도착 한 날 짜 는 6.9 호 이다.
select
passenger_phone,
max(min_gt50_dt) as min_gt50_dt,
max(min_gt100_dt) as min_gt100_dt
from
(
select
*,
min(dt) over(partition by passenger_phone,if(cost_until_today >= 50,1,0)) as min_gt50_dt,
min(dt) over(partition by passenger_phone,if(cost_until_today >= 100,1,0)) as min_gt100_dt
from
(
select
dt,
passenger_phone,
cost,
sum(cost) over(partition by passenger_phone order by dt) as cost_until_today
from
tmp_time_exp
)aa
)bb
group by
passenger_phone
결실
passenger_phone min_gt50_dt min_gt100_dt
156 20190602 20190609
173 20190609 20190609
187 20190603 20190604
그 중에서 가장 핵심 적 인 것 은
sum() over(partition by ... order by dt)
문 구 를 사용 하여 dt
까지 의 조별 총 화 를 나타 내 는 것 이다. 즉, 누적 마감 표현 이다. 일부 분 구 경계 에 대한 한정 적 인 고려 는 다음 과 같은 7 번 째 문 제 를 참고 할 수 있다.7. 특정 시간 대 내 소비의 최대 치 찾기
예: 예 를 들 어 하나의 요 구 는 6.5 호 전후 3 일 중 소비 금액 이 가장 큰 날 을 찾 는 것 이다. 이런 구간 의 성질 이 가장 큰 값 을 찾 으 면 대략 창 함수 로 이 루어 진다.
max() over(partition by ... order by dt rows between 3 preceding and 3 following)
와 비슷 하 게 dt
이날 까지 3 일 앞으로 미 루 고 3 일 뒤로 미 루 는 것 을 나타 낸다. 즉, 총 7 일 (자신 포함) 내 에서 이 구간 의 최대 치 를 찾 는 것 이다.같은 이치 로 창 집합 을 sum
으로 바 꾸 면 이 구간 내의 총화 가 된다.select
dt,
passenger_phone,
cost,
max(cost) over(partition by passenger_phone order by dt rows between unbounded preceding and current row) as until_cur_max,
max(cost) over(partition by passenger_phone order by dt) as until_cur_max2, --
max(cost) over(partition by passenger_phone order by dt rows between 3 preceding and 3 following) as before3later3_max,
sum(cost) over(partition by passenger_phone order by dt rows between 3 preceding and 3 following) as before3later3_sum
from
tmp_time_exp
결실
dt passenger_phone cost until_cur_max until_cur_max2 before3later3_max before3later3_sum
20190531 156 20 20 20 20 50
20190601 156 20 20 20 20 50
20190602 156 10 20 20 20 60
20190603 156 0 20 20 20 70
20190604 156 0 20 20 20 60
20190605 156 10 20 20 10 40
20190606 156 10 20 20 20 50
20190607 156 10 20 20 20 70
20190608 156 0 20 20 20 70
20190609 156 20 20 20 20 60
20190610 156 20 20 20 20 50
20190609 173 0 0 0 10 10
20190610 173 10 10 10 10 10
20190531 187 0 0 0 30 60
20190601 187 10 10 10 40 100
20190602 187 20 20 20 40 100
20190603 187 30 30 30 40 110
20190604 187 40 40 40 40 110
20190605 187 0 40 40 40 120
20190606 187 10 40 40 40 120
20190607 187 0 40 40 40 100
20190608 187 20 40 40 20 60
20190609 187 20 40 40 20 60
20190610 187 10 40 40 20 50
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Redash를 사용할 때 몰랐던 SQL을 쓰는 법을 배웠습니다.최근 redash에서 sql을 쓸 기회가 많고, 이런 쓰는 방법이 있었는지와 sql에 대해 공부를 다시하고 있기 때문에 배운 것을 여기에 씁니다. Redash란? 월별로 데이터를 표시하고 싶습니다 주별로 데이터를 표...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.