1. 程式人生 > 其它 >數倉工具—Hive語法之視窗函式練習和總結(15)

數倉工具—Hive語法之視窗函式練習和總結(15)

技術標籤:資料倉庫Hivehive大資料資料倉庫面試

視窗函式練習

視窗函式其實日常中用的是比較多的,加上之前我們分別介紹了各個視窗函式,今天我們就練習和總結一下

題目

題目一:每個使用者截止到每月為止的最大交易金額和該月的累積總交易金額

資料來源格式如下

表名表註釋欄位欄位註釋
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_keyumonth(當月)ucount(月訂單量)current_max(最大交易金額)current_sum(累計該月總交易金額)
110092018-12153.9953.99
13589992019-2128.9928.99
13589992019-4169.9998.98
13590002019-112294.992294.99
13590022019-1118.998.99
13590032020-111120.491120.49
13590052019-21782.99782.99
13590092019-112384.072384.07
13590142019-1169.9969.99
13590142019-2169.9969.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)

這樣就可以了

題目二:求使用者號對應不同的產品

資料來源格式如下

使用者號產品購買時間
1A2019-12-23
1B2019-12-23
2C2019-12-23
2A2019-12-24
2B2019-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
;

image-20210109111333057

接下來我們可以使用自關聯來獲取下一個使用的產品,因為是自關聯所以我們可以使用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
;

image-20210109112136015

接下來你只要使用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
;

image-20210109112904606

上面的結果還是需要再處理一步

但是需要注意的是, 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)層次查詢

常見的視窗函式

彙總函式:

  1. sum(col) over() : 分組對col求和,over() 中的語法如下
  2. count(col) over() : 分組對col求個數,over() 中的語法如下
  3. min(col) over() : 分組對col求最小值
  4. max(col) over() : 分組求col的最大值
  5. avg(col) over() : 分組求col列的平均值

獲取特定記錄函式:

  1. first_value(col) over() : 某分割槽排序後的第一個col值
  2. last_value(col) over() : 某分割槽排序後的最後一個col值
  3. lag(col,n,DEFAULT) : 統計往前n行的col值,n可選,預設為1,DEFAULT當往上第n行為NULL時候,取預設值,如不指定,則為NULL
  4. lead(col,n,DEFAULT) : 統計往後n行的col值,n可選,預設為1,DEFAULT當往下第n行為NULL時候,取預設值,如不指定,則為NULL

分片函式:

  1. ntile(n) : 用於將分組資料按照順序切分成n片,返回當前切片值。注意:n必須為int型別。

排名函式

  1. row_number() over() : 排名函式,不會重複,適合於生成主鍵或者不併列排名
  2. rank() over() : 排名函式,有並列名次,名次不連續。如:1,1,3
  3. dense_rank() over() : 排名函式,有並列名次,名次連續。如:1,1,2

計算百分比函式:

  1. cume_dist
  2. percent_rank