數倉工具—Hive語法之視窗函式練習和總結(15)
阿新 • • 發佈:2021-01-11
視窗函式練習
視窗函式其實日常中用的是比較多的,加上之前我們分別介紹了各個視窗函式,今天我們就練習和總結一下
題目
題目一:每個使用者截止到每月為止的最大交易金額和該月的累積總交易金額
資料來源格式如下
表名 | 表註釋 | 欄位 | 欄位註釋 | |
---|---|---|---|---|
ods_sales_orders | 訂單明細表 | sales_order_key | 訂單主鍵 | 一個訂單表示銷售一個產品 |
ods_sales_orders | 訂單明細表 | create_date | 訂單日期 | |
ods_sales_orders | 訂單明細表 | customer_key | 客戶編號 | |
ods_sales_orders | 訂單明細表 | product_key | 產品編號 | |
ods_sales_orders | 訂單明細表 | english_product_name | 產品名 | |
ods_sales_orders | 訂單明細表 | cpzl_zw | 產品子類 | |
ods_sales_orders | 訂單明細表 | cplb_zw | 產品類別 | |
ods_sales_orders | 訂單明細表 | unit_price | 產品單價 |
輸出結果資料格式如下
customer_key | umonth(當月) | ucount(月訂單量) | current_max(最大交易金額) | current_sum(累計該月總交易金額) |
---|---|---|---|---|
11009 | 2018-12 | 1 | 53.99 | 53.99 |
1358999 | 2019-2 | 1 | 28.99 | 28.99 |
1358999 | 2019-4 | 1 | 69.99 | 98.98 |
1359000 | 2019-1 | 1 | 2294.99 | 2294.99 |
1359002 | 2019-11 | 1 | 8.99 | 8.99 |
1359003 | 2020-1 | 1 | 1120.49 | 1120.49 |
1359005 | 2019-2 | 1 | 782.99 | 782.99 |
1359009 | 2019-1 | 1 | 2384.07 | 2384.07 |
1359014 | 2019-1 | 1 | 69.99 | 69.99 |
1359014 | 2019-2 | 1 | 69.99 | 69.99 |
思路:
- 1.對資料按照客戶及其年-月分組
- 2.分組後就每月銷售金額之和
- 3.使用視窗函式,對每個客戶不同月份分組求最大值(max)和累計值(sum)
解答:
select t.customer_key, t.umonth, t.ucount,
max(current_max) over(partition by t.customer_key order by umonth) as current_max,
sum(current_sum) over(partition by t.customer_key order by umonth) as current_sum
from
(
select
customer_key, substr(create_date,1,7) as umonth,
count(sales_order_key) as ucount,
max(unit_price) as current_max,
sum(unit_price) as current_sum
from
adventure_ods.ods_sales_orders
group by
customer_key, substr(create_date,1,7)
) tpm;
備註:也可以使用with tmp as () 句法:
with tmp as (
select
customer_key, substr(create_date,1,7) as umonth,
count(sales_order_key) as ucount,
max(unit_price) as current_max,
sum(unit_price) as current_sum
from
adventure_ods.ods_sales_orders
group by
customer_key, substr(create_date,1,7)
)
select
customer_key, umonth, ucount,
max(current_max) over(partition by customer_key order by umonth) as current_max,
sum(current_sum) over(partition by customer_key order by umonth) as current_sum
from tmp limit 10;
點評:
上面的寫法就是為了視窗函式而視窗函式的,而且還計算錯了你看到最後的結果是累積和最大是相等的,問題出在那裡了呢,1 這個需求是一個彙總性質的結果,所以我們可以不用使用視窗函式 2 子查詢過後每個使用者每個月就只有一條資料了
select
customer_key, substr(create_date,1,7) as umonth,
count(sales_order_key) as ucount,
max(unit_price) as current_max,
sum(unit_price) as current_sum
from
adventure_ods.ods_sales_orders
group by
customer_key, substr(create_date,1,7)
這樣就可以了
題目二:求使用者號對應不同的產品
資料來源格式如下
使用者號 | 產品 | 購買時間 |
---|---|---|
1 | A | 2019-12-23 |
1 | B | 2019-12-23 |
2 | C | 2019-12-23 |
2 | A | 2019-12-24 |
2 | B | 2019-12-23 |
create table ods_user_product_log(
userid string,
product string,
ctm string
) row format delimited fields terminated by ',';
LOAD DATA LOCAL INPATH '/Users/liuwenqiang/workspace/hive/ods_user_product_log.txt' OVERWRITE INTO TABLE ods_user_product_log;
要求輸出例子:使用者號-產品1-產品2(前兩個產品)
**例如:**1-A-B (按先後時間順序,實現相同時不限定順序)
思路:
- 1.利用視窗函式,對使用者號分組,按時間對產品進行排序
- 2.利用左連或其他方法拼接,然後進行篩選
- 3.用concat或者其他函式拼接獲得結果
select
userid,product,row_number() over (partition by userid order by ctm) as rn
from
ods_user_product_log
;
接下來我們可以使用自關聯來獲取下一個使用的產品,因為是自關聯所以我們可以使用with as 的寫法
with tmp as (
select
userid,product,row_number() over (partition by userid order by ctm) as rn
from
ods_user_product_log
)
select
a.userid,a.product,b.product
from
tmp a
inner join
tmp b
on
a.userid=b.userid
where
a.rn=1
and b.rn=2
;
接下來你只要使用concat_ws 就可以了
其實前面我們說的 lead,lag 在很多場合下可以替代自關聯,接下來我們看看怎麼使用lead來完成上面的需求
select concat_ws('-', a.userid, a.product, a.next_product)
from (
select a.userid,
a.product,
lead(product, 1) over (partition by userid order by ctm) as next_product
from ods_user_product_log a
) a
where next_product is not null
;
上面的結果還是需要再處理一步
但是需要注意的是, lead,lag 雖好,但是就上面這個例子,如果我要計算的不是前兩個產品,而是全部產品,那你就要使用自關聯了
題目三:查詢5月份購買過的顧客及總人數
其實我們看到這個題目就是一個普通的聚合操作,因為這裡是顧客明細,不是購買明細
select
a.customer_key,count(customer_key)
from
ods_sales_orders a
where
month(create_date)='05'
group by
a.customer_key
;
當然如果你是在想用視窗實現也不是不可以
select
customer_key,
count(*) over()
from
ods_sales_orders
where
month(create_date)="05"
group by
customer_key
;
題目四:查詢顧客的購買明細及月購買總額
這才是一個非常典型的視窗函式應用,明細和彙總資料都需要的場景
select
* ,
sum(unit_price) over(partition by customer_key,substr(create_date,1,7))
from
ods_sales_orders
;
題目五:查詢顧客的購買明細及當月累積購買總額和月購買總額
select
* ,
sum(unit_price) over(partition by customer_key,substr(create_date,1,7) sort by create_date rows between unbounded preceding and current row ) as sumcost
from
ods_sales_orders
題目六:查詢顧客上次的購買時間
提示:使用偏移的視窗函式lag()
select
* ,
lag(create_date,1) over(partition by customer_key sort by create_date) as last_time
from
ods_sales_orders
題目七:查詢最近前20%時間的訂單資訊
提示:使用ntile(x):分割x份。x是整數。
用ntile函式將訂單時間按順序分為5堆
select * from (
select
*,
ntile(5) over(sort by create_date asc) as five_num
from
ods_sales_orders
) t
where
five_num = 1
總結
視窗函式應用場景
(1)用於分割槽排序
(2)動態Group By
(3)Top N
(4)累計計算
(5)層次查詢
常見的視窗函式
彙總函式:
- sum(col) over() : 分組對col求和,over() 中的語法如下
- count(col) over() : 分組對col求個數,over() 中的語法如下
- min(col) over() : 分組對col求最小值
- max(col) over() : 分組求col的最大值
- avg(col) over() : 分組求col列的平均值
獲取特定記錄函式:
- first_value(col) over() : 某分割槽排序後的第一個col值
- last_value(col) over() : 某分割槽排序後的最後一個col值
- lag(col,n,DEFAULT) : 統計往前n行的col值,n可選,預設為1,DEFAULT當往上第n行為NULL時候,取預設值,如不指定,則為NULL
- lead(col,n,DEFAULT) : 統計往後n行的col值,n可選,預設為1,DEFAULT當往下第n行為NULL時候,取預設值,如不指定,則為NULL
分片函式:
- ntile(n) : 用於將分組資料按照順序切分成n片,返回當前切片值。注意:n必須為int型別。
排名函式:
- row_number() over() : 排名函式,不會重複,適合於生成主鍵或者不併列排名
- rank() over() : 排名函式,有並列名次,名次不連續。如:1,1,3
- dense_rank() over() : 排名函式,有並列名次,名次連續。如:1,1,2
計算百分比函式:
- cume_dist
- percent_rank