Hive 之 函數 02-常用查詢函數(二)

六、 窗口函數

6.1 函數說明

OVER(): 指定分析函數工作的數據窗口大小,這個數據窗口大小可能會隨着行的變化而變化; 如果括號內爲空, 表示對整個數據集開窗;

over()括號內可以寫的參數:

CURRENT ROW: 當前行;
n PRECEDING: 往前 n 行數據;
n FOLLOWING: 往後 n 行數據;
UNBOUNDED: 起點, UNBOUNDED PRECEDING表示從前面的起點, UNBOUNDED FOLLOWING表示到後面的終點;(如果想要計算某個時間段內整體的購買額, 使用 distribute bysort by又只能“累加”, 此時就可使用“起點”到“終點”這個)

over()前面可以寫的參數:

LAG(col,n): 往前第 n 行數據; 可以接受第三個參數, 當爲 Null 時充當默認值;
LEAD(col,n): 往後第 n 行數據; 可以接受第三個參數, 當爲 Null 時充當默認值;
NTILE(n): 把有序分區中的行分發到指定數據的組中,各個組有編號,編號從 1 開始, 對於每一行, NTILE 返回此行所屬的組的編號。 注意: n 必須爲 int 類型。 主要用於求百分率內數據信息的情況。

【注】over()放在聚合函數的後面, 它的作用是開窗, 範圍僅僅是針對它前面的聚合函數有效;

6.2 需求

○ 查詢在 2017 年 4 月份購買過的顧客及總人數
○ 查詢顧客的購買明細及購買總額
○ 上述的場景, 要將 cost 按照日期進行逐個累加
○ 查詢顧客上次的購買時間
○ 查詢前 20% 時間的訂單信息

6.3 實現

建表及導入數據:

hive (default)> create table business(
              > name string, 
              > orderdate string, 
              > cost int)
              > row format delimited fields terminated by ',';
OK
Time taken: 0.884 seconds

hive (default)> load data local inpath 
              > '/opt/module/data/business.txt'
              > into table business;
Loading data to table default.business
Table default.business stats: [numFiles=1, totalSize=171]
OK
Time taken: 0.419 seconds

查詢全部數據:

hive (default)> select * from business;
OK
business.name	business.orderdate	business.cost
jack	2017-01-01	10
tony	2017-01-02	15
jack	2017-02-03	23
tony	2017-01-04	29
jack	2017-01-05	46
jack	2017-04-06	42
tony	2017-01-07	50
jack	2017-01-08	55
mart	2017-04-08	62
mart	2017-04-09	68
neil	2017-05-10	12
mart	2017-04-11	75
neil	2017-06-12	80
mart	2017-04-13	94
Time taken: 2.0 seconds, Fetched: 14 row(s)

6.3.1 查詢在 2017 年 4 月份購買過的顧客及總人數

hive(default)> select name, count(1) over()
              > from business
              > where substring(orderdate, 1, 7) = '2017-04'
              > group by name;

... ...
OK
name	count_window_0
mart	2
jack	2
Time taken: 114.428 seconds, Fetched: 2 row(s)

【注】over()是開窗函數, 針對【每一條】數據進行開窗, 如果括號內沒內容, 就會將所有符合 where 子句及後續限定語句的數據作爲開窗的數據給前面的聚合函數進行聚合計算; 本例中, mart 購買過 4 次, jack 購買過 1 次, 符合 where 子句的共有 5 條數據, 加上 group by的限制, 就變成了兩條(因爲相同的 name 就只輸出了一條數據, 總共兩條), 所以第二列的 count 函數就會輸出 2。

如果本例中去掉 group by的語句, 那麼就會將符合 where 子句的所有數據進行開窗(5 條), 那麼結果的第一列會出現一個 jack 和四個 mart, 而第二列就應該都是 5。

hive (default)> select name, count(*) over()
              > from business
              > where substring(orderdate, 1, 7) = '2017-04';

... ...
OK
name	count_window_0
mart	5
mart	5
mart	5
mart	5
jack	5
Time taken: 23.664 seconds, Fetched: 5 row(s)

如果不加開窗函數 over(), 那麼結果將是 2017 年 4 月份購買過的人, 及購買過的次數:

hive (default)> select name, count(*)
              > from business
              > where substring(orderdate, 1, 7) = '2017-04'
              > group by name;

... ...
OK
name	_c1
jack	1
mart	4
Time taken: 19.944 seconds, Fetched: 2 row(s)

6.3.2 查詢顧客的購買明細及購買總額

hive (default)> select name, orderdate, cost, sum(cost) over()
              > from business;

... ...
OK
name	orderdate	cost	sum_window_0
mart	2017-04-13	94	661
neil	2017-06-12	80	661
mart	2017-04-11	75	661
neil	2017-05-10	12	661
mart	2017-04-09	68	661
mart	2017-04-08	62	661
jack	2017-01-08	55	661
tony	2017-01-07	50	661
jack	2017-04-06	42	661
jack	2017-01-05	46	661
tony	2017-01-04	29	661
jack	2017-02-03	23	661
tony	2017-01-02	15	661
jack	2017-01-01	10	661
Time taken: 18.86 seconds, Fetched: 14 row(s)

6.3.3 上述的場景, 要將 cost 按照日期進行逐個累加

hive (default)> select orderdate, cost, sum(cost) over(order by orderdate)
              > from business;

... ...
OK
orderdate	cost	sum_window_0
2017-01-01	10	10
2017-01-02	15	25
2017-01-04	29	54
2017-01-05	46	100
2017-01-07	50	150
2017-01-08	55	205
2017-02-03	23	228
2017-04-06	42	270
2017-04-08	62	332
2017-04-09	68	400
2017-04-11	75	475
2017-04-13	94	569
2017-05-10	12	581
2017-06-12	80	661
Time taken: 22.812 seconds, Fetched: 14 row(s)

【注】在 over()函數中進行 order by的時候, 第一次的數據只有日期最小的, 結果就爲 10, 第二次, 包含了最小和倒數第二小的日期的數據, 所以結果就是 10+15=25, 以此類推。

假如我現在想得到每個人的明細, 及每個人各自的總消費額, 則有:

hive (default)> select name, orderdate, cost, sum(cost) over(distribute by name)
              > from business;

... ...
OK
name	orderdate	cost	sum_window_0
jack	2017-01-05	46	176
jack	2017-01-08	55	176
jack	2017-01-01	10	176
jack	2017-04-06	42	176
jack	2017-02-03	23	176
mart	2017-04-13	94	299
mart	2017-04-11	75	299
mart	2017-04-09	68	299
mart	2017-04-08	62	299
neil	2017-05-10	12	92
neil	2017-06-12	80	92
tony	2017-01-04	29	94
tony	2017-01-02	15	94
tony	2017-01-07	50	94
Time taken: 19.979 seconds, Fetched: 14 row(s)

【注】窗口函數中的條件是按照 name 進行分區, 那麼結果就是計算每個分區後單獨分區的 cost 總和了。 不能在 over()函數中使用 group by, 會報錯!

假如現在想要每個人的購買明細, 並且按照購買日期排序, 同時還要把每個人的購買額逐一累加結果展示出來:

hive (default)> select name, orderdate, cost, sum(cost) over(distribute by name sort by orderdate)
              > from business;

... ...
OK
name	orderdate	cost	sum_window_0
jack	2017-01-01	10	10
jack	2017-01-05	46	56
jack	2017-01-08	55	111
jack	2017-02-03	23	134
jack	2017-04-06	42	176
mart	2017-04-08	62	62
mart	2017-04-09	68	130
mart	2017-04-11	75	205
mart	2017-04-13	94	299
neil	2017-05-10	12	12
neil	2017-06-12	80	92
tony	2017-01-02	15	15
tony	2017-01-04	29	44
tony	2017-01-07	50	94
Time taken: 30.149 seconds, Fetched: 14 row(s)

6.3.4 查詢顧客上次的購買時間

hive (default)> select name, orderdate, cost, 
              > lag(orderdate, 1) over(distribute by name sort by orderdate)
              > from business;

... ...
OK
name	orderdate	cost	lag_window_0
jack	2017-01-01	10	NULL
jack	2017-01-05	46	2017-01-01
jack	2017-01-08	55	2017-01-05
jack	2017-02-03	23	2017-01-08
jack	2017-04-06	42	2017-02-03
mart	2017-04-08	62	NULL
mart	2017-04-09	68	2017-04-08
mart	2017-04-11	75	2017-04-09
mart	2017-04-13	94	2017-04-11
neil	2017-05-10	12	NULL
neil	2017-06-12	80	2017-05-10
tony	2017-01-02	15	NULL
tony	2017-01-04	29	2017-01-02
tony	2017-01-07	50	2017-01-04
Time taken: 14.933 seconds, Fetched: 14 row(s)

【注】要想不出現 Null, 可以在 lag 函數中傳第三個參數, 可作爲一個默認值, 如果遇到 Null, 則會以默認值替代;

如果需求變爲 “查詢顧客下次的購買時間” 就將 lag 函數換成 lead 即可;

6.3.5 查詢前 20% 時間的訂單信息

首先, 將數據分成五 “組”, 使用 ntile()函數即可:

hive (default)> select name, orderdate, cost, 
              > ntile(5) over(order by orderdate)
              > from business;

... ...
OK
name	orderdate	cost	ntile_window_0
jack	2017-01-01	10	1
tony	2017-01-02	15	1
tony	2017-01-04	29	1
jack	2017-01-05	46	2
tony	2017-01-07	50	2
jack	2017-01-08	55	2
jack	2017-02-03	23	3
jack	2017-04-06	42	3
mart	2017-04-08	62	3
mart	2017-04-09	68	4
mart	2017-04-11	75	4
mart	2017-04-13	94	4
neil	2017-05-10	12	5
neil	2017-06-12	80	5
Time taken: 54.086 seconds, Fetched: 14 row(s)

從五組裏面取出第一組的數據, 即是 “前 20%” 時間的數據:

hive (default)> select name, orderdate, cost
              > from 
              > (
              > select name, orderdate, cost, 
              > ntile(5) over(order by orderdate) ntile_5
              > from business
              > ) t1
              > where t1.ntile_5 = 1;

... ...
OK
name	orderdate	cost
jack	2017-01-01	10
tony	2017-01-02	15
tony	2017-01-04	29
Time taken: 24.67 seconds, Fetched: 3 row(s)

6.3.6 關於幾個時間參數的使用示例

select name,orderdate,cost,
sum(cost) over() as sample1,--所有行相加
sum(cost) over(partition by name) as sample2,--按 name 分組,組內數據相加
sum(cost) over(partition by name order by orderdate) as sample3,--按 name 分組,組內數據累加
sum(cost) over(partition by name order by orderdate rows between
UNBOUNDED PRECEDING and current row ) as sample4 ,--和 sample3 一樣,由起點到當前行的聚合
sum(cost) over(partition by name order by orderdate rows between
1 PRECEDING and current row) as sample5, --當前行和前面一行做聚合
sum(cost) over(partition by name order by orderdate rows between 
1 PRECEDING AND 1 FOLLOWING ) as sample6,--當前行和前邊一行及後面一行
sum(cost) over(partition by name order by orderdate rows between
current row and UNBOUNDED FOLLOWING ) as sample7 --當前行及後面所有行
from business;

6.3.7 SQL 語句的書寫及執行順序

書寫順序:(gohl)

select
from 
join on
where
group by 
order by
having
limit

執行順序:(gshol)

from
join on
where
group by
select
having
order by
limit

七、 排名函數

首先要說明的是下面介紹的排名函數也是窗口函數中的, 只不過爲了凸顯它們的排名功能, 所以給單列出來了。

7.1 函數說明

RANK(): 排序相同時會重複,總數不會變; (有並列第一時是 1, 1, 3, 4, … …)

DENSE_RANK(): 排序相同時會重複,總數會減少; (有並列第一時是 1, 1, 2, 3, … …)

ROW_NUMBER(): 會根據順序計算; (有並列第一時也是 1, 2, 3, 4, … …)

7.2 需求

計算每門學科成績排名。

7.3 實現

建表並導入數據:

hive (default)> create table score
              > (
              > name string, 
              > subject string, 
              > score int
              > )
              > row format delimited fields terminated by '\t';
OK
Time taken: 1.336 seconds

hive (default)> load data local inpath 
              > '/opt/module/data/subject.txt'
              > into table score;
Loading data to table default.score
Table default.score stats: [numFiles=1, totalSize=213]
OK
Time taken: 0.52 seconds

查詢數據:

hive (default)> select * from score;
OK
score.name	score.subject	score.score
孫悟空	語文	87
孫悟空	數學	95
孫悟空	英語	68
大海	語文	94
大海	數學	56
大海	英語	84
宋宋	語文	64
宋宋	數學	86
宋宋	英語	84
婷婷	語文	65
婷婷	數學	85
婷婷	英語	78
Time taken: 0.094 seconds, Fetched: 12 row(s)

按需求查詢數據:

hive (default)> select name, subject, score, 
              > rank() over(partition by subject order by score desc) rank_f, 
              > dense_rank() over(distribute by subject sort by score desc) dense_rank_f, 
              > row_number() over(partition by subject order by score desc) row_number_f
              > from score;

... ...
OK
name	subject	score	rank_f	dense_rank_f	row_number_f
孫悟空	數學	95	1	1	1
宋宋	數學	86	2	2	2
婷婷	數學	85	3	3	3
大海	數學	56	4	4	4
宋宋	英語	84	1	1	1
大海	英語	84	1	1	2
婷婷	英語	78	3	2	3
孫悟空	英語	68	4	3	4
大海	語文	94	1	1	1
孫悟空	語文	87	2	2	2
婷婷	語文	65	3	3	3
宋宋	語文	64	4	4	4
Time taken: 30.053 seconds, Fetched: 12 row(s)

八、 兩個小題

8.1 統計用戶累計訪問次數

建表及導入數據:

hive (default)> create table action
              > (
              > userid string, 
              > visitdate string,
              > visitcount int
              > )
              > row format delimited fields terminated by '\t';
OK
Time taken: 0.079 seconds
hive (default)> load data local inpath 
              > '/opt/module/data/visit.txt'
              > into table action;
Loading data to table default.action
Table default.action stats: [numFiles=1, totalSize=128]
OK
Time taken: 0.206 seconds

查詢數據:

hive (default)> select * from action;
OK
action.userid	action.visitdate	action.visitcount
u01	2017/1/21	5
u02	2017/1/23	6
u03	2017/1/22	8
u04	2017/1/20	3
u01	2017/1/23	6
u01	2017/2/21	8
u02	2017/1/23	6
u01	2017/2/22	4
Time taken: 0.054 seconds, Fetched: 8 row(s)

需求: 統計出每個用戶、 每個月的累計訪問次數:

首先, 將日期修改爲最終需要的格式:

hive (default)> select userid, 
              > date_format(regexp_replace(visitdate, '/', '-'), 'yyyy-MM') mn, 
              > visitcount
              > from action;
OK
userid	mn	visitcount
u01	2017-01	5
u02	2017-01	6
u03	2017-01	8
u04	2017-01	3
u01	2017-01	6
u01	2017-02	8
u02	2017-01	6
u01	2017-02	4
Time taken: 0.065 seconds, Fetched: 8 row(s)

其次, 求出第三列:

hive (default)> select userid, mn, sum(visitcount)
              > from 
              > (
              > select userid, 
              > date_format(regexp_replace(visitdate, '/', '-'),'yyyy-MM') mn,
              > visitcount
              > from action
              > ) t1
              > group by userid, mn;

... ...
OK
userid	mn	_c2
u01	2017-01	11
u01	2017-02	12
u02	2017-01	12
u03	2017-01	8
u04	2017-01	3
Time taken: 17.183 seconds, Fetched: 5 row(s)

最後, 求出第四列:

hive (default)> select userid, mn, sum_c, sum(sum_c) over(distribute by userid sort by mn)
              > from 
              > (
              > select userid, mn, sum(visitcount) sum_c
              > from (
              > select userid,
              > date_format(regexp_replace(visitdate, '/', '-'),'yyyy-MM') mn,
              > visitcount
              > from action) t1
              > group by userid, mn
              > ) t2;

... ...
OK
userid	mn	sum_c	sum_window_0
u01	2017-01	11	11
u01	2017-02	12	23
u02	2017-01	12	12
u03	2017-01	8	8
u04	2017-01	3	3
Time taken: 15.256 seconds, Fetched: 5 row(s)

8.2 店鋪訪客數等

有 50W個店鋪,每個顧客訪問任何一個店鋪的任何一個商品時都會產生一條訪問日誌,訪問日誌存儲的表名爲 jd, 訪客的用戶 id 爲 user_id, 被訪問的店鋪名稱爲 shop, 請統計:

  1. 每個店鋪的UV(訪客數)

  2. 每個店鋪訪問次數top3的訪客信息。輸出店鋪名稱、訪客id、訪問次數

建表及導入數據:

hive (default)> create table jd(
              > usr_id string, 
              > shop string
              > )
              > row format delimited fields terminated by '\t';
OK
Time taken: 0.679 seconds

hive (default)> load data local inpath
              > '/opt/module/data/jd.txt'
              > into table jd;
Loading data to table default.jd
Table default.jd stats: [numFiles=1, totalSize=95]
OK
Time taken: 0.34 seconds

查詢數據:

hive (default)> select * from jd;
OK
jd.usr_id	jd.shop
u1	a
u2	b
u1	b
u1	a
u3	c
u4	b
u1	a
u2	c
u5	b
u4	b
u6	c
u2	c
u1	b
u2	a
u2	a
u3	a
u5	a
u5	a
u5	a
Time taken: 0.079 seconds, Fetched: 19 row(s)

需求一: 每個店鋪的UV(訪客數)

先按照用戶、商鋪去重:

hive (default)> select usr_id, shop 
              > from jd
              > group by usr_id, shop;

... ...
OK
usr_id	shop
u1	a
u1	b
u2	a
u2	b
u2	c
u3	a
u3	c
u4	b
u5	a
u5	b
u6	c
Time taken: 15.159 seconds, Fetched: 11 row(s)

然後計數:

hive (default)> select shop, count(1) uv from 
              > (
              > select usr_id, shop 
              > from jd
              > group by usr_id, shop
              > ) t1
              > group by shop;

... ...
OK
shop	uv
a	4
b	4
c	3
Time taken: 64.018 seconds, Fetched: 3 row(s)

需求二: 每個店鋪訪問次數 top3 的訪客信息。輸出店鋪名稱、訪客id、訪問次數

先計算每個商鋪中每個賬戶的訪問次數:

hive (default)> select shop, usr_id, count(*)
              > from jd
              > group by shop, usr_id;

... ...
OK
shop	usr_id	_c2
a	u1	3
a	u2	2
a	u3	1
a	u5	3
b	u1	2
b	u2	1
b	u4	2
b	u5	1
c	u2	2
c	u3	1
c	u6	1
Time taken: 44.766 seconds, Fetched: 11 row(s)

針對同一店鋪, 對訪問次數進行逆序排序, 並新增一個 rank 排序列:

hive (default)> select shop, usr_id, uv, row_number() over(distribute by shop sort by uv desc)
              > from (
              > select shop, usr_id, count(*) uv
              > from jd
              > group by shop, usr_id
              > ) t1;

... ...
OK
shop	usr_id	uv	row_number_window_0
a	u5	3	1
a	u1	3	2
a	u2	2	3
a	u3	1	4
b	u4	2	1
b	u1	2	2
b	u5	1	3
b	u2	1	4
c	u2	2	1
c	u6	1	2
c	u3	1	3
Time taken: 47.539 seconds, Fetched: 11 row(s)

從上一步的數據中, 拿出 top3:

hive (default)> select shop, usr_id, uv
              > from 
              > (
              > select shop, usr_id, uv, row_number() over(distribute by shop sort by uv desc) rk
              > from (
              > select shop, usr_id, count(*) uv
              > from jd
              > group by shop, usr_id) t1
              > ) t2
              > where rk < 4;

... ...
OK
shop	usr_id	uv
a	u5	3
a	u1	3
a	u2	2
b	u4	2
b	u1	2
b	u5	1
c	u2	2
c	u6	1
c	u3	1
Time taken: 73.195 seconds, Fetched: 9 row(s)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章