最強最全面的Hive SQL開發(fā)指南,四萬字全面解析
本文整體分為兩部分,第一部分是簡寫,如果能看懂會用,就直接從此部分查,方便快捷,如果不是很理解此SQl的用法,則查看第二部分,是詳細說明,當然第二部分語句也會更全一些!
第一部分:
hive模糊搜索表:show tables like '*name*';
查看表結構信息:desc table_name;
查看分區(qū)信息:show partitions table_name;
加載本地文件:load data local inpath '/xxx/test.txt' overwrite into table dm.table_name;
從查詢語句給table插入數據:insert overwrite table table_name partition(dt) select * from table_name;
導出數據到本地系統:insert overwrite local directory '/tmp/text' select a.* from table_name a order by 1;
創(chuàng)建表時指定的一些屬性:
字段分隔符:row format delimited fields terminated by ' '
行分隔符:row format delimited lines terminated by ''
文件格式為文本型存儲:stored as textfile
命令行操作:hive -e 'select table_cloum from table'執(zhí)行一個查詢,在終端上顯示mapreduce的進度,執(zhí)行完畢后,最后把查詢結果輸出到終端上,接著hive進程退出,不會進入交互模式
hive -S -e 'select table_cloum from table' -S,終端上的輸出不會有mapreduce的進度,執(zhí)行完畢,只會把查詢結果輸出到終端上。
hive修改表名:alter table old_table_name rename to new_table_name;
hive復制表結構:create table new_table_name like table_name;
hive添加字段:alter table table_name add columns(columns_values bigint comment 'comm_text');
hive修改字段:alter table table_name change old_column new_column string comment 'comm_text';
刪除分區(qū):alter table table_name drop partition(dt='2021-11-30');
添加分區(qū):alter table table_name add partition (dt='2021-11-30');
刪除空數據庫:drop database myhive2;
強制刪除數據庫:drop database myhive2 cascade;
刪除表:drop table score5;
清空表:truncate table score6;
向hive表中加載數據
直接向分區(qū)表中插入數據:insert into table score partition(month ='202107') values ('001','002','100');通過load方式加載數據:load data local inpath '/export/servers/hivedatas/score.csv' overwrite into table score partition(month='201806');通過查詢方式加載數據:insert overwrite table score2 partition(month = '202106') select s_id,c_id,s_score from score1;查詢語句中創(chuàng)建表并加載數據:create table score2 as select * from score1;在創(chuàng)建表是通過location指定加載數據的路徑:create external table score6 (s_id string,c_id string,s_score int) row format delimited fields terminated by ',' location '/myscore';export導出與import 導入 hive表數據(內部表操作):
create table techer2 like techer; --依據已有表結構創(chuàng)建表
export table techer to '/export/techer';
import table techer2 from '/export/techer';
20. hive表中數據導出
insert導出
將查詢的結果導出到本地:insert overwrite local directory '/export/servers/exporthive' select * from score;
將查詢的結果格式化導出到本地:insert overwrite local directory '/export/servers/exporthive' row format delimited fields terminated by ' ' collection items terminated by '#' select * from student;
將查詢的結果導出到HDFS上(沒有l(wèi)ocal):insert overwrite directory '/export/servers/exporthive' row format delimited fields terminated by ' ' collection items terminated by '#' select * from score;
Hadoop命令導出到本地:dfs -get /export/servers/exporthive/000000_0 /export/servers/exporthive/local.txt;hive shell 命令導出
基本語法:(hive -f/-e 執(zhí)行語句或者腳本 > file)hive -e "select * from myhive.score;" > /export/servers/exporthive/score.txt
hive -f export.sh > /export/servers/exporthive/score.txt
export導出到HDFS上:export table score to '/export/exporthive/score';Hive查詢語句
GROUP BY 分組:select s_id ,avg(s_score) avgscore from score group by s_id having avgscore > 85; 對分組后的數據進行篩選,使用 having
join 連接:inner join 內連接;left join 左連接;right join 右鏈接;full join 全外鏈接。
order by 排序:ASC(ascend): 升序(默認) DESC(descend): 降序
sort by 局部排序:每個MapReduce內部進行排序,對全局結果集來說不是排序。
distribute by 分區(qū)排序:類似MR中partition,進行分區(qū),結合sort by使用
Hive函數1. 聚合函數
指定列值的數目:count()
指定列值求和:sum()
指定列的最大值:max()
指定列的最小值:min()
指定列的平均值:avg()
非空集合總體變量函數:var_pop(col)
非空集合樣本變量函數:var_samp (col)
總體標準偏離函數:stddev_pop(col)
分位數函數:percentile(BIGINT col, p)
中位數函數:percentile(BIGINT col, 0.5)
2. 關系運算
A LIKE B:LIKE比較,如果字符串A符合表達式B 的正則語法,則為TRUE
A RLIKE B:JAVA的LIKE操作,如果字符串A符合JAVA正則表達式B的正則語法,則為TRUE
A REGEXP B:功能與RLIKE相同
3. 數學運算
支持所有數值類型:加(+)、減(-)、乘(*)、除(/)、取余(%)、位與(&)、位或(|)、位異或(^)、位取反(~)
4. 邏輯運算
支持:邏輯與(and)、邏輯或(or)、邏輯非(not)
5. 數值運算
取整函數:round(double a)
指定精度取整函數:round(double a, int d)
向下取整函數:floor(double a)
向上取整函數:ceil(double a)
取隨機數函數:rand(),rand(int seed)
自然指數函數:exp(double a)
以10為底對數函數:log10(double a)
以2為底對數函數:log2()
對數函數:log()
冪運算函數:pow(double a, double p)
開平方函數:sqrt(double a)
二進制函數:bin(BIGINT a)
十六進制函數:hex()
絕對值函數:abs()
正取余函數:pmod()
6. 條件函數
if
case when
coalesce(c1,c2,c3)
nvl(c1,c2)
7. 日期函數
獲得當前時區(qū)的UNIX時間戳: unix_timestamp()
時間戳轉日期函數:from_unixtime()
日期轉時間戳:unix_timestamp(string date)
日期時間轉日期函數:to_date(string timestamp)
日期轉年函數:year(string date)
日期轉月函數:month (string date)
日期轉天函數: day (string date)
日期轉小時函數: hour (string date)
日期轉分鐘函數:minute (string date)
日期轉秒函數: second (string date)
日期轉周函數: weekofyear (string date)
日期比較函數: datediff(string enddate, string startdate)
日期增加函數: date_add(string startdate, int days)
日期減少函數:date_sub (string startdate, int days)
8. 字符串函數
字符串長度函數:length(string A)
字符串反轉函數:reverse(string A)
字符串連接函數: concat(string A, string B…)
帶分隔符字符串連接函數:concat_ws(string SEP, string A, string B…)
字符串截取函數: substr(string A, int start, int len)
字符串轉大寫函數: upper(string A)
字符串轉小寫函數:lower(string A)
去空格函數:trim(string A)
左邊去空格函數:ltrim(string A)
右邊去空格函數:rtrim(string A)
正則表達式替換函數:regexp_replace(string A, string B, string C)
正則表達式解析函數: regexp_extract(string subject, string pattern, int index)
URL解析函數:parse_url(string urlString, string partToExtract [, string keyToExtract])返回值: string
json解析函數:get_json_object(string json_string, string path)
空格字符串函數:space(int n)
重復字符串函數:repeat(string str, int n)
首字符ascii函數:ascii(string str)
左補足函數:lpad(string str, int len, string pad)
右補足函數:rpad(string str, int len, string pad)
分割字符串函數: split(string str, string pat)
集合查找函數: find_in_set(string str, string strList)
9. 窗口函數
分組求和函數:sum(pv) over(partition by cookieid order by createtime) 有坑,加不加 order by 差別很大,具體詳情在下面第二部分。
分組內排序,從1開始順序排:ROW_NUMBER() 如:1234567
分組內排序,排名相等會在名次中留下空位:RANK() 如:1233567
分組內排序,排名相等不會在名次中留下空位:DENSE_RANK() 如:1233456
有序的數據集合平均分配到指定的數量(num)個桶中:NTILE()
統計窗口內往上第n行值:LAG(col,n,DEFAULT)
統計窗口內往下第n行值:LEAD(col,n,DEFAULT)
分組內排序后,截止到當前行,第一個值:FIRST_VALUE(col)
分組內排序后,截止到當前行,最后一個值: LAST_VALUE(col)
小于等于當前值的行數/分組內總行數:CUME_DIST()
以下函數建議看第二部分詳細理解下,此處僅簡寫,!
將多個group by 邏輯寫在一個sql語句中: GROUPING SETS
根據GROUP BY的維度的所有組合進行聚合:CUBE
CUBE的子集,以最左側的維度為主,從該維度進行層級聚合:ROLLUP
第二部分1. 對數據庫的操作創(chuàng)建數據庫:create database if not exists myhive;
說明:hive的表存放位置模式是由hive-site.xml當中的一個屬性指定的 :hive.metastore.warehouse.dir
創(chuàng)建數據庫并指定hdfs存儲位置 :
create database myhive2 location '/myhive2';
修改數據庫:alter database myhive2 set dbproperties('createtime'='20210329');
說明:可以使用alter database 命令來修改數據庫的一些屬性。但是數據庫的元數據信息是不可更改的,包括數據庫的名稱以及數據庫所在的位置
查看數據庫詳細信息查看數據庫基本信息
hive (myhive)> desc database myhive2;
查看數據庫更多詳細信息
hive (myhive)> desc database extended myhive2;
刪除數據庫刪除一個空數據庫,如果數據庫下面有數據表,那么就會報錯
drop database myhive2;
強制刪除數據庫,包含數據庫下面的表一起刪除
drop database myhive cascade;
注意:
如果使用 group by 分組,則 select 后面只能寫分組的字段或者聚合函數
where和having區(qū)別:
1 having是在 group by 分完組之后再對數據進行篩選,所以having 要篩選的字段只能是分組字段或者聚合函數
2 where 是從數據表中的字段直接進行的篩選的,所以不能跟在gruopby后面,也不能使用聚合函數
join 連接INNER JOIN 內連接:只有進行連接的兩個表中都存在與連接條件相匹配的數據才會被保留下來
select * from techer t [inner] join course c on t.t_id = c.t_id; -- inner 可省略
LEFT OUTER JOIN 左外連接:左邊所有數據會被返回,右邊符合條件的被返回
select * from techer t left join course c on t.t_id = c.t_id; -- outer可省略
RIGHT OUTER JOIN 右外連接:右邊所有數據會被返回,左邊符合條件的被返回、
select * from techer t right join course c on t.t_id = c.t_id;
FULL OUTER JOIN 滿外(全外)連接: 將會返回所有表中符合條件的所有記錄。如果任一表的指定字段沒有符合條件的值的話,那
就使用NULL值替代。
SELECT * FROM techer t FULL JOIN course c ON t.t_id = c.t_id ;
注:1. hive2版本已經支持不等值連接,就是 join on條件后面可以使用大于小于符號了;并且也支持 join on 條件后跟or (早前版本 on 后只支持 = 和 and,不支持 > < 和 or)
2.如hive執(zhí)行引擎使用MapReduce,一個join就會啟動一個job,一條sql語句中如有多個join,則會啟動多個job
注意:表之間用逗號(,)連接和 inner join 是一樣的
select * from table_a,table_b where table_a.id=table_b.id;
它們的執(zhí)行效率沒有區(qū)別,只是書寫方式不同,用逗號是sql 89標準,join 是sql 92標準。用逗號連接后面過濾條件用 where ,用 join 連接后面過濾條件是 on。
order by 排序全局排序,只會有一個reduce
ASC(ascend): 升序(默認) DESC(descend): 降序
SELECT * FROM student s LEFT JOIN score sco ON s.s_id = sco.s_id ORDER BY sco.s_score DESC;
注意:order by 是全局排序,所以最后只有一個reduce,也就是在一個節(jié)點執(zhí)行,如果數據量太大,就會耗費較長時間
sort by 局部排序每個MapReduce內部進行排序,對全局結果集來說不是排序。
設置reduce個數
set mapreduce.job.reduces=3;
查看設置reduce個數
set mapreduce.job.reduces;
查詢成績按照成績降序排列
select * from score sort by s_score;
將查詢結果導入到文件中(按照成績降序排列)
insert overwrite local directory '/export/servers/hivedatas/sort' select * from score sort by s_score;
distribute by 分區(qū)排序distribute by:類似MR中partition,進行分區(qū),結合sort by使用
設置reduce的個數,將我們對應的s_id劃分到對應的reduce當中去
set mapreduce.job.reduces=7;
通過distribute by 進行數據的分區(qū)
* from score distribute by s_id sort by s_score;
注意:Hive要求 distribute by 語句要寫在 sort by 語句之前
cluster by當distribute by和sort by字段相同時,可以使用cluster by方式.
cluster by除了具有distribute by的功能外還兼具sort by的功能。但是排序只能是正序排序,不能指定排序規(guī)則為ASC或者DESC。
以下兩種寫法等價
select * from score cluster by s_id;
select * from score distribute by s_id sort by s_id;
Hive函數聚合函數hive支持 count(),max(),min(),sum(),avg() 等常用的聚合函數
注意:
聚合操作時要注意null值
count(*) 包含null值,統計所有行數
count(id) 不包含null值
min 求最小值是不包含null,除非所有值都是null
avg 求平均值也是不包含null
非空集合總體變量函數: var_pop語法: var_pop(col)
返回值: double
說明: 統計結果集中col非空集合的總體變量(忽略null)
非空集合樣本變量函數: var_samp語法: var_samp (col)
返回值: double
說明: 統計結果集中col非空集合的樣本變量(忽略null)
總體標準偏離函數: stddev_pop語法: stddev_pop(col)
返回值: double
說明: 該函數計算總體標準偏離,并返回總體變量的平方根,其返回值與VAR_POP函數的平方根相同
中位數函數: percentile語法: percentile(BIGINT col, p)
返回值: double
說明: 求準確的第pth個百分位數,p必須介于0和1之間,但是col字段目前只支持整數,不支持浮點數類型
關系運算支持:等值(=)、不等值(。 或 <>)、小于(<)、小于等于(<=)、大于(>)、大于等于(>=)
空值判斷(is null)、非空判斷(is not null)
LIKE比較: LIKE語法: A LIKE B
操作類型: strings
描述: 如果字符串A或者字符串B為NULL,則返回NULL;如果字符串A符合表達式B 的正則語法,則為TRUE;否則為FALSE。B中字符”_”表示任意單個字符,而字符”%”表示任意數量的字符。
JAVA的LIKE操作: RLIKE語法: A RLIKE B
操作類型: strings
描述: 如果字符串A或者字符串B為NULL,則返回NULL;如果字符串A符合JAVA正則表達式B的正則語法,則為TRUE;否則為FALSE。
REGEXP操作: REGEXP語法: A REGEXP B
操作類型: strings
描述: 功能與RLIKE相同
示例:select 1 from tableName where 'footbar' REGEXP '^f.*r$';
結果:1
數學運算支持所有數值類型:加(+)、減(-)、乘(*)、除(/)、取余(%)、位與(&)、位或(|)、位異或(^)、位取反(~)
邏輯運算支持:邏輯與(and)、邏輯或(or)、邏輯非(not)
數值運算取整函數: round語法: round(double a)
返回值: BIGINT
說明: 返回double類型的整數值部分 (遵循四舍五入)
示例:select round(3.1415926) from tableName;
結果:3
指定精度取整函數: round語法: round(double a, int d)
返回值: DOUBLE
說明: 返回指定精度d的double類型
hive> select round(3.1415926,4) from tableName;
3.1416
向下取整函數: floor語法: floor(double a)
返回值: BIGINT
說明: 返回等于或者小于該double變量的最大的整數
hive> select floor(3.641) from tableName;
3
向上取整函數: ceil語法: ceil(double a)
返回值: BIGINT
說明: 返回等于或者大于該double變量的最小的整數
hive> select ceil(3.1415926) from tableName;
4
取隨機數函數: rand語法: rand(),rand(int seed)
返回值: double
說明: 返回一個0到1范圍內的隨機數。如果指定種子seed,則會等到一個穩(wěn)定的隨機數序列
hive> select rand() from tableName; -- 每次執(zhí)行此語句得到的結果都不同
0.5577432776034763
hive> select rand(100) ; -- 只要指定種子,每次執(zhí)行此語句得到的結果一樣的
0.7220096548596434
自然指數函數: exp語法: exp(double a)
返回值: double
說明: 返回自然對數e的a次方
hive> select exp(2) ;
7.38905609893065
以10為底對數函數: log10語法: log10(double a)
返回值: double
說明: 返回以10為底的a的對數
hive> select log10(100) ;
2.0
此外還有:以2為底對數函數: log2()、對數函數: log()
冪運算函數: pow語法: pow(double a, double p)
返回值: double
說明: 返回a的p次冪
hive> select pow(2,4) ;
16.0
開平方函數: sqrt語法: sqrt(double a)
返回值: double
說明: 返回a的平方根
hive> select sqrt(16) ;
4.0
二進制函數: bin語法: bin(BIGINT a)
返回值: string
說明: 返回a的二進制代碼表示
hive> select bin(7) ;
111
hive (hive_explode)> select get_json_object(concat('{',sale_info_1,'}'),'$.source') as source,get_json_object(concat('{',sale_info_1,'}'),'$.monthSales') as monthSales,get_json_object(concat('{',sale_info_1,'}'),'$.userCount') as monthSales,get_json_object(concat('{',sale_info_1,'}'),'$.score') as monthSales from explode_lateral_view LATERAL VIEW explode(split(regexp_replace(regexp_replace(sale_info,'\[\{',''),'}]',''),'},\{'))sale_info as sale_info_1;
總結:
Lateral View通常和UDTF一起出現,為了解決UDTF不允許在select字段的問題。Multiple Lateral View可以實現類似笛卡爾乘積。Outer關鍵字可以把不輸出的UDTF的空結果,輸出成NULL,防止丟失數據。
行轉列
相關參數說明:
CONCAT(string A/col, string B/col…):返回輸入字符串連接后的結果,支持任意個輸入字符串;
CONCAT_WS(separator, str1, str2,...):它是一個特殊形式的 CONCAT()。第一個參數剩余參數間的分隔符。分隔符可以是與剩余參數一樣的字符串。如果分隔符是 NULL,返回值也將為 NULL。這個函數會跳過分隔符參數后的任何 NULL 和空字符串。分隔符將被加到被連接的字符串之間;
COLLECT_SET(col):函數只接受基本數據類型,它的主要作用是將某字段的值進行去重匯總,產生array類型字段。
數據準備:
nameconstellationblood_type孫悟空白羊座A老王射手座A宋宋白羊座B豬八戒白羊座A鳳姐射手座A
需求: 把星座和血型一樣的人歸類到一起。結果如下:
射手座,A 老王|鳳姐
白羊座,A 孫悟空|豬八戒
白羊座,B 宋宋
實現步驟:
創(chuàng)建本地constellation.txt,導入數據node03服務器執(zhí)行以下命令創(chuàng)建文件,注意數據使用 進行分割
cd /export/servers/hivedatas
vim constellation.txt
數據如下:
孫悟空 白羊座 A
老王 射手座 A
宋宋 白羊座 B
豬八戒 白羊座 A
鳳姐 射手座 A
創(chuàng)建hive表并導入數據創(chuàng)建hive表并加載數據
hive (hive_explode)> create table person_info(
name string,
constellation string,
blood_type string)
row format delimited fields terminated by " ";
加載數據
hive (hive_explode)> load data local inpath '/export/servers/hivedatas/constellation.txt' into table person_info;
按需求查詢數據hive (hive_explode)> select
t1.base,
concat_ws('|', collect_set(t1.name)) name
from
(select
name,
concat(constellation, "," , blood_type) base
from
person_info) t1
group by
t1.base;
列轉行
所需函數:
EXPLODE(col):將hive一列中復雜的array或者map結構拆分成多行。
LATERAL VIEW
用法:LATERAL VIEW udtf(expression) tableAlias AS columnAlias
解釋:用于和split, explode等UDTF一起使用,它能夠將一列數據拆成多行數據,在此基礎上可以對拆分后的數據進行聚合。
數據準備:
cd /export/servers/hivedatas
vim movie.txt
文件內容如下: 數據字段之間使用 進行分割
《疑犯追蹤》 懸疑,動作,科幻,劇情
《Lie to me》 懸疑,警匪,動作,心理,劇情
《戰(zhàn)狼2》 戰(zhàn)爭,動作,災難
需求: 將電影分類中的數組數據展開。結果如下:
《疑犯追蹤》 懸疑
《疑犯追蹤》 動作
《疑犯追蹤》 科幻
《疑犯追蹤》 劇情
《Lie to me》 懸疑
《Lie to me》 警匪
《Lie to me》 動作
《Lie to me》 心理
《Lie to me》 劇情
《戰(zhàn)狼2》 戰(zhàn)爭
《戰(zhàn)狼2》 動作
《戰(zhàn)狼2》 災難
實現步驟:
創(chuàng)建hive表create table movie_info(
movie string,
category array<string>)
row format delimited fields terminated by " "
collection items terminated by ",";
加載數據load data local inpath "/export/servers/hivedatas/movie.txt" into table movie_info;
按需求查詢數據select
movie,
category_name
from
movie_info lateral view explode(category) table_tmp as category_name;
reflect函數
reflect函數可以支持在sql中調用java中的自帶函數,秒殺一切udf函數。
需求1: 使用java.lang.Math當中的Max求兩列中最大值
實現步驟:
創(chuàng)建hive表create table test_udf(col1 int,col2 int) row format delimited fields terminated by ',';
準備數據并加載數據cd /export/servers/hivedatas
vim test_udf
文件內容如下:
1,2
4,3
6,4
7,5
5,6
加載數據hive (hive_explode)> load data local inpath '/export/servers/hivedatas/test_udf' overwrite into table test_udf;
使用java.lang.Math當中的Max求兩列當中的最大值hive (hive_explode)> select reflect("java.lang.Math","max",col1,col2) from test_udf;
需求2: 文件中不同的記錄來執(zhí)行不同的java的內置函數
實現步驟:
創(chuàng)建hive表hive (hive_explode)> create table test_udf2(class_name string,method_name string,col1 int , col2 int) row format delimited fields terminated by ',';
準備數據cd /export/servers/hivedatas
vim test_udf2
文件內容如下:
java.lang.Math,min,1,2
java.lang.Math,max,2,3
加載數據hive (hive_explode)> load data local inpath '/export/servers/hivedatas/test_udf2' overwrite into table test_udf2;
執(zhí)行查詢hive (hive_explode)> select reflect(class_name,method_name,col1,col2) from test_udf2;
需求3: 判斷是否為數字
實現方式:
使用apache commons中的函數,commons下的jar已經包含在hadoop的classpath中,所以可以直接使用。
select reflect("org.apache.commons.lang.math.NumberUtils","isNumber","123")
Hive 窗口函數
窗口函數最重要的關鍵字是 partition by 和 order by
具體語法如下:XXX over (partition by xxx order by xxx)
特別注意:over()里面的 partition by 和 order by 都不是必選的,over()里面可以只有partition by,也可以只有order by,也可以兩個都沒有,大家需根據需求靈活運用。
窗口函數我劃分了幾個大類,我們一類一類的講解。
1. SUM、AVG、MIN、MAX
講解這幾個窗口函數前,先創(chuàng)建一個表,以實際例子講解大家更容易理解。
首先創(chuàng)建用戶訪問頁面表:user_pv
create table user_pv(
cookieid string, -- 用戶登錄的cookie,即用戶標識
createtime string, -- 日期
pv int -- 頁面訪問量
);
給上面這個表加上如下數據:
cookie1,2021-05-10,1
cookie1,2021-05-11,5
cookie1,2021-05-12,7
cookie1,2021-05-13,3
cookie1,2021-05-14,2
cookie1,2021-05-15,4
cookie1,2021-05-16,4
SUM()使用
執(zhí)行如下查詢語句:
select cookieid,createtime,pv,
sum(pv) over(partition by cookieid order by createtime) as pv1
from user_pv;
結果如下:(因命令行原因,下圖字段名和值是錯位的,請注意辨別!)
執(zhí)行如下查詢語句:
select cookieid,createtime,pv,
sum(pv) over(partition by cookieid ) as pv1
from user_pv;
結果如下:
第一條SQL的over()里面加 order by ,第二條SQL沒加order by ,結果差別很大
所以要注意了:
over()里面加 order by 表示:分組內從起點到當前行的pv累積,如,11號的pv1=10號的pv+11號的pv, 12號=10號+11號+12號;
over()里面不加 order by 表示:將分組內所有值累加。
AVG,MIN,MAX,和SUM用法一樣,這里就不展開講了,但是要注意 AVG,MIN,MAX 的over()里面加不加 order by 也和SUM一樣,如 AVG 求平均值,如果加上 order by,表示分組內從起點到當前行的平局值,不是全部的平局值。MIN,MAX 同理。
2. ROW_NUMBER、RANK、DENSE_RANK、NTILE
還是用上述的用戶登錄日志表:user_pv,里面的數據換成如下所示:
cookie1,2021-05-10,1
cookie1,2021-05-11,5
cookie1,2021-05-12,7
cookie1,2021-05-13,3
cookie1,2021-05-14,2
cookie1,2021-05-15,4
cookie1,2021-05-16,4
cookie2,2021-05-10,2
cookie2,2021-05-11,3
cookie2,2021-05-12,5
cookie2,2021-05-13,6
cookie2,2021-05-14,3
cookie2,2021-05-15,9
cookie2,2021-05-16,7
ROW_NUMBER()使用:
ROW_NUMBER()從1開始,按照順序,生成分組內記錄的序列。
SELECT
cookieid,
createtime,
pv,
ROW_NUMBER() OVER(PARTITION BY cookieid ORDER BY pv desc) AS rn
FROM user_pv;
結果如下:
RANK 和 DENSE_RANK 使用:
RANK() 生成數據項在分組中的排名,排名相等會在名次中留下空位。
DENSE_RANK()生成數據項在分組中的排名,排名相等會在名次中不會留下空位。
SELECT
cookieid,
createtime,
pv,
RANK() OVER(PARTITION BY cookieid ORDER BY pv desc) AS rn1,
DENSE_RANK() OVER(PARTITION BY cookieid ORDER BY pv desc) AS rn2,
ROW_NUMBER() OVER(PARTITION BY cookieid ORDER BY pv DESC) AS rn3
FROM user_pv
WHERE cookieid = 'cookie1';
結果如下:
NTILE的使用:
有時會有這樣的需求:如果數據排序后分為三部分,業(yè)務人員只關心其中的一部分,如何將這中間的三分之一數據拿出來呢?NTILE函數即可以滿足。
ntile可以看成是:把有序的數據集合平均分配到指定的數量(num)個桶中, 將桶號分配給每一行。如果不能平均分配,則優(yōu)先分配較小編號的桶,并且各個桶中能放的行數最多相差1。
然后可以根據桶號,選取前或后 n分之幾的數據。數據會完整展示出來,只是給相應的數據打標簽;具體要取幾分之幾的數據,需要再嵌套一層根據標簽取出。
SELECT
cookieid,
createtime,
pv,
NTILE(2) OVER(PARTITION BY cookieid ORDER BY createtime) AS rn1,
NTILE(3) OVER(PARTITION BY cookieid ORDER BY createtime) AS rn2,
NTILE(4) OVER(ORDER BY createtime) AS rn3
FROM user_pv
ORDER BY cookieid,createtime;
結果如下:
3. LAG、LEAD、FIRST_VALUE、LAST_VALUE
講解這幾個窗口函數時還是以實例講解,首先創(chuàng)建用戶訪問頁面表:user_url
CREATE TABLE user_url (
cookieid string,
createtime string, --頁面訪問時間
url string --被訪問頁面
);
表中加入如下數據:
cookie1,2021-06-10 10:00:02,url2
cookie1,2021-06-10 10:00:00,url1
cookie1,2021-06-10 10:03:04,1url3
cookie1,2021-06-10 10:50:05,url6
cookie1,2021-06-10 11:00:00,url7
cookie1,2021-06-10 10:10:00,url4
cookie1,2021-06-10 10:50:01,url5
cookie2,2021-06-10 10:00:02,url22
cookie2,2021-06-10 10:00:00,url11
cookie2,2021-06-10 10:03:04,1url33
cookie2,2021-06-10 10:50:05,url66
cookie2,2021-06-10 11:00:00,url77
cookie2,2021-06-10 10:10:00,url44
cookie2,2021-06-10 10:50:01,url55
LAG的使用:
LAG(col,n,DEFAULT) 用于統計窗口內往上第n行值。
第一個參數為列名,第二個參數為往上第n行(可選,默認為1),第三個參數為默認值(當往上第n行為NULL時候,取默認值,如不指定,則為NULL)
SELECT cookieid,
createtime,
url,
ROW_NUMBER() OVER(PARTITION BY cookieid ORDER BY createtime) AS rn,
LAG(createtime,1,'1970-01-01 00:00:00') OVER(PARTITION BY cookieid ORDER BY createtime) AS last_1
time,
LAG(createtime,2) OVER(PARTITION BY cookieid ORDER BY createtime) AS last_2_time
FROM user_url;
結果如下:
解釋:
last_1_time: 指定了往上第1行的值,default為'1970-01-01 00:00:00'
cookie1第一行,往上1行為NULL,因此取默認值 1970-01-01 00:00:00
cookie1第三行,往上1行值為第二行值,2021-06-10 10:00:02
cookie1第六行,往上1行值為第五行值,2021-06-10 10:50:01
last_2_time: 指定了往上第2行的值,為指定默認值
cookie1第一行,往上2行為NULL
cookie1第二行,往上2行為NULL
cookie1第四行,往上2行為第二行值,2021-06-10 10:00:02
cookie1第七行,往上2行為第五行值,2021-06-10 10:50:01
LEAD的使用:
與LAG相反
LEAD(col,n,DEFAULT) 用于統計窗口內往下第n行值。
第一個參數為列名,第二個參數為往下第n行(可選,默認為1),第三個參數為默認值(當往下第n行為NULL時候,取默認值,如不指定,則為NULL)
SELECT cookieid,
createtime,
url,
ROW_NUMBER() OVER(PARTITION BY cookieid ORDER BY createtime) AS rn,
LEAD(createtime,1,'1970-01-01 00:00:00') OVER(PARTITION BY cookieid ORDER BY createtime) AS next__time,
LEAD(createtime,2) OVER(PARTITION BY cookieid ORDER BY createtime) AS next_2_time
FROM user_url;
結果如下:
FIRST_VALUE的使用:
取分組內排序后,截止到當前行,第一個值。
SELECT cookieid,
createtime,
url,
ROW_NUMBER() OVER(PARTITION BY cookieid ORDER BY createtime) AS rn,
FIRST_VALUE(url) OVER(PARTITION BY cookieid ORDER BY createtime) AS first1
FROM user_url;
結果如下:
LAST_VALUE的使用:
取分組內排序后,截止到當前行,最后一個值。
SELECT cookieid,
createtime,
url,
ROW_NUMBER() OVER(PARTITION BY cookieid ORDER BY createtime) AS rn,
LAST_VALUE(url) OVER(PARTITION BY cookieid ORDER BY createtime) AS last1
FROM user_url;
結果如下:
如果想要取分組內排序后最后一個值,則需要變通一下:
SELECT cookieid,
createtime,
url,
ROW_NUMBER() OVER(PARTITION BY cookieid ORDER BY createtime) AS rn,
LAST_VALUE(url) OVER(PARTITION BY cookieid ORDER BY createtime) AS last1,
FIRST_VALUE(url) OVER(PARTITION BY cookieid ORDER BY createtime DESC) AS last2
FROM user_url
ORDER BY cookieid,createtime;
注意上述SQL,使用的是 FIRST_VALUE 的倒序取出分組內排序最后一個值!
結果如下:
此處要特別注意order by
如果不指定ORDER BY,則進行排序混亂,會出現錯誤的結果
SELECT cookieid,
createtime,
url,
FIRST_VALUE(url) OVER(PARTITION BY cookieid) AS first2
FROM user_url;
結果如下:
上述 url2 和 url55 的createtime即不屬于最靠前的時間也不屬于最靠后的時間,所以結果是混亂的。
4. CUME_DIST
先創(chuàng)建一張員工薪水表:staff_salary
CREATE EXTERNAL TABLE staff_salary (
dept string,
userid string,
sal int
);
表中加入如下數據:
d1,user1,1000
d1,user2,2000
d1,user3,3000
d2,user4,4000
d2,user5,5000
CUME_DIST的使用:
此函數的結果和order by的排序順序有關系。
CUME_DIST:小于等于當前值的行數/分組內總行數。order默認順序:正序
比如,統計小于等于當前薪水的人數,所占總人數的比例。
SELECT
dept,
userid,
sal,
CUME_DIST() OVER(ORDER BY sal) AS rn1,
CUME_DIST() OVER(PARTITION BY dept ORDER BY sal) AS rn2
FROM staff_salary;
結果如下:
解釋:
rn1: 沒有partition,所有數據均為1組,總行數為5,
第一行:小于等于1000的行數為1,因此,1/5=0.2
第三行:小于等于3000的行數為3,因此,3/5=0.6
rn2: 按照部門分組,dpet=d1的行數為3
第二行:小于等于2000的行數為2,因此,2/3=0.6666666666666666
5. GROUPING SETS、GROUPING__ID、CUBE、ROLLUP
這幾個分析函數通常用于OLAP中,不能累加,而且需要根據不同維度上鉆和下鉆的指標統計,比如,分小時、天、月的UV數。
還是先創(chuàng)建一個用戶訪問表:user_date
CREATE TABLE user_date (
month STRING,
day STRING,
cookieid STRING
);
表中加入如下數據:
2021-03,2021-03-10,cookie1
2021-03,2021-03-10,cookie5
2021-03,2021-03-12,cookie7
2021-04,2021-04-12,cookie3
2021-04,2021-04-13,cookie2
2021-04,2021-04-13,cookie4
2021-04,2021-04-16,cookie4
2021-03,2021-03-10,cookie2
2021-03,2021-03-10,cookie3
2021-04,2021-04-12,cookie5
2021-04,2021-04-13,cookie6
2021-04,2021-04-15,cookie3
2021-04,2021-04-15,cookie2
2021-04,2021-04-16,cookie1
GROUPING SETS的使用:
grouping sets是一種將多個group by 邏輯寫在一個sql語句中的便利寫法。
等價于將不同維度的GROUP BY結果集進行UNION ALL。
SELECT
month,
day,
COUNT(DISTINCT cookieid) AS uv,
GROUPING__ID
FROM user_date
GROUP BY month,day
GROUPING SETS (month,day)
ORDER BY GROUPING__ID;
注:上述SQL中的GROUPING__ID,是個關鍵字,表示結果屬于哪一個分組集合,根據grouping sets中的分組條件month,day,1是代表month,2是代表day。
結果如下:
上述SQL等價于:
SELECT month,
NULL as day,
COUNT(DISTINCT cookieid) AS uv,
1 AS GROUPING__ID
FROM user_date
GROUP BY month
UNION ALL
SELECT NULL as month,
day,
COUNT(DISTINCT cookieid) AS uv,
2 AS GROUPING__ID
FROM user_date
GROUP BY day;
CUBE的使用:
根據GROUP BY的維度的所有組合進行聚合。
SELECT
month,
day,
COUNT(DISTINCT cookieid) AS uv,
GROUPING__ID
FROM user_date
GROUP BY month,day
WITH CUBE
ORDER BY GROUPING__ID;
結果如下:
上述SQL等價于:
SELECT NULL,NULL,COUNT(DISTINCT cookieid) AS uv,0 AS GROUPING__ID FROM user_date
UNION ALL
SELECT month,NULL,COUNT(DISTINCT cookieid) AS uv,1 AS GROUPING__ID FROM user_date GROUP BY month
UNION ALL
SELECT NULL,day,COUNT(DISTINCT cookieid) AS uv,2 AS GROUPING__ID FROM user_date GROUP BY day
UNION ALL
SELECT month,day,COUNT(DISTINCT cookieid) AS uv,3 AS GROUPING__ID FROM user_date GROUP BY month,day;
ROLLUP的使用:
是CUBE的子集,以最左側的維度為主,從該維度進行層級聚合。
比如,以month維度進行層級聚合:
SELECT
month,
day,
COUNT(DISTINCT cookieid) AS uv,
GROUPING__ID
FROM user_date
GROUP BY month,day
WITH ROLLUP
ORDER BY GROUPING__ID;
結果如下:
把month和day調換順序,則以day維度進行層級聚合:
SELECT
day,
month,
COUNT(DISTINCT cookieid) AS uv,
GROUPING__ID
FROM user_date
GROUP BY day,month
WITH ROLLUP
ORDER BY GROUPING__ID;
結果如下:
這里,根據日和月進行聚合,和根據日聚合結果一樣,因為有父子關系,如果是其他維度組合的話,就會不一樣。

請輸入評論內容...
請輸入評論/評論長度6~500個字
最新活動更多
-
10月23日火熱報名中>> 2025是德科技創(chuàng)新技術峰會
-
10月23日立即報名>> Works With 開發(fā)者大會深圳站
-
10月24日立即參評>> 【評選】維科杯·OFweek 2025(第十屆)物聯網行業(yè)年度評選
-
11月27日立即報名>> 【工程師系列】汽車電子技術在線大會
-
12月18日立即報名>> 【線下會議】OFweek 2025(第十屆)物聯網產業(yè)大會
-
精彩回顧立即查看>> 【限時福利】TE 2025國際物聯網展·深圳站