大資料開發之Spark SQL/Hive實用函式分享
字串函式
1. concat
對字串進行拼接:concat(str1, str2, ..., strN) ,引數:str1、str2...是要進行拼接的字串。
-- return the concatenation of str1、str2、
2. concat_ws
在拼接的字串中間新增某種分隔符:concat_ws(sep, [str | array(str)]+)。
引數1:分隔符,如 - ;引數2:要拼接的字串(可多個)
-- return the concatenation of the strings separated by sep
-- Spark-SQL
select concat_ws("-", "Spark", "SQL");
3. encode
設定編碼格式:encode(str, charset)。
引數1:要進行編碼的字串 ;引數2:使用的編碼格式,如UTF-8
-- encode the first argument using the second argument character set
select encode("HIVE", "UTF-8");
4. decode
轉碼:decode(bin, charset)。
引數1:進行轉碼的binary ;引數2:使用的轉碼格式,如UTF-8
-- decode the first argument using the second argument character set
select decode(encode("HIVE", "UTF-8"), "UTF-8");
5. format_string / printf
格式化字串:format_string(strfmt, obj, ...)
-- returns a formatted string from printf-style format strings
select format_string("Spark SQL %d %s", 100, "days");
6. initcap / lower / upper
initcap:將每個單詞的首字母轉為大寫,其他字母小寫。單詞之間以空白分隔。
upper:全部轉為大寫。
lower:全部轉為小寫。
-- Spark Sql
select initcap("spaRk sql");
-- SPARK SQL
select upper("sPark sql");
-- spark sql
select lower("Spark Sql");
7. length
返回字串的長度。
-- 返回4
select length("Hive");
8. lpad / rpad
返回固定長度的字串,如果長度不夠,用某種字元進行補全。
lpad(str, len, pad):左補全
rpad(str, len, pad):右補全
注意:如果引數str的長度大於引數len,則返回的結果長度會被擷取為長度為len的字串
-- vehi
select lpad("hi", 4, "ve");
-- hive
select rpad("hi", 4, "ve");
-- spar
select lpad("spark", 4, "ve");
9. trim / ltrim / rtrim
去除空格或者某種字元。
trim(str) / trim(trimStr, str):首尾去除。
ltrim(str) / ltrim(trimStr, str):左去除。
rtrim(str) / rtrim(trimStr, str):右去除。
-- hive
select trim(" hive ");
-- arkSQLS
SELECT ltrim("Sp", "SSparkSQLS") as tmp;
10. regexp_extract
正則提取某些字串
-- 2000
select regexp_extract("1000-2000", "(\\d+)-(\\d+)", 2);
11. regexp_replace
正則替換
-- r-r
select regexp_replace("100-200", "(\\d+)", "r");
12. repeat
repeat(str, n):複製給定的字串n次
-- aa
select repeat("a", 2);
13. instr / locate
返回擷取字串的位置。如果匹配的字串不存在,則返回0
-- returns the (1-based) index of the first occurrence of substr in str.
-- 6
select instr("SparkSQL", "SQL");
-- 0
select locate("A", "fruit");
14. space
在字串前面加n個空格
select concat(space(2), "A");
15. split
split(str, regex):以某字元拆分字串 split(str, regex)
-- ["one","two"]
select split("one two", " ");
16. substr / substring_index
-- k SQL
select substr("Spark SQL", 5);
-- 從後面開始擷取,返回SQL
select substr("Spark SQL", -3);
-- k
select substr("Spark SQL", 5, 1);
-- org.apache。注意:如果引數3為負值,則從右邊取值
select substring_index("org.apache.spark", ".", 2);
17. translate
替換某些字元為指定字元
-- The translate will happen when any character in the string matches the character in the `matchingString`
-- A1B2C3
select translate("AaBbCc", "abc", "123");
JSON函式
1. get_json_object
-- v2
select get_json_object('{"k1": "v1", "k2": "v2"}', '$.k2');
2. from_json
select tmp.k from (
select from_json('{"k": "fruit", "v": "apple"}','k STRING, v STRING', map("","")) as tmp
);
這個方法可以給json定義一個Schema,這樣在使用時,就可以直接使用a.k這種方式了,會簡化很多。
3. to_json
-- 可以把所有欄位轉化為json字串,然後表示成value欄位
select to_json(struct(*)) AS value;
時間函式
1. current_date / current_timestamp
獲取當前時間
select current_date;
select current_timestamp;
2. 從日期時間中提取欄位/格式化時間
1)year、month、day、dayofmonth、hour、minute、second
-- 20
select day("2020-12-20");
2)dayofweek(1 = Sunday, 2 = Monday, ..., 7 = Saturday)、dayofyear
-- 7
select dayofweek("2020-12-12");
3)weekofyear(date)
/**
* Extracts the week number as an integer from a given date/timestamp/string.
*
* A week is considered to start on a Monday and week 1 is the first week with more than 3 days,
* as defined by ISO 8601
*
* @return An integer, or null if the input was a string that could not be cast to a date
* @group datetime_funcs
* @since 1.5.0
*/
def weekofyear(e: Column): Column = withExpr { WeekOfYear(e.expr) }
-- 50
select weekofyear("2020-12-12");
4)trunc
擷取某部分的日期,其他部分預設為01。第二個引數: YEAR、YYYY、YY、MON、MONTH、MM
-- 2020-01-01
select trunc("2020-12-12", "YEAR");
-- 2020-12-01
select trunc("2020-12-12", "MM");
5)date_trunc
引數:YEAR、YYYY、YY、MON、MONTH、MM、DAY、DD、HOUR、MINUTE、SECOND、WEEK、QUARTER
-- 2012-12-12 09:00:00
select date_trunc("HOUR" ,"2012-12-12T09:32:05.359");
6)date_format
按照某種格式格式化時間
-- 2020-12-12
select date_format("2020-12-12 12:12:12", "yyyy-MM-dd");
3. 日期時間轉換
1)unix_timestamp
返回當前時間的unix時間戳。
select unix_timestamp();
-- 1609257600
select unix_timestamp("2020-12-30", "yyyy-MM-dd");
2)from_unixtime
將unix epoch(1970-01-01 00:00:00 UTC)中的秒數轉換為以給定格式表示當前系統時區中該時刻的時間戳的字串。
select from_unixtime(1609257600, "yyyy-MM-dd HH:mm:ss");
3)to_unix_timestamp
將時間轉化為時間戳。
-- 1609257600
select to_unix_timestamp("2020-12-30", "yyyy-MM-dd");
4)to_date / date
將時間字串轉化為date。
-- 2020-12-30
select to_date("2020-12-30 12:30:00");
select date("2020-12-30");
5)to_timestamp
將時間字串轉化為timestamp。
select to_timestamp("2020-12-30 12:30:00");
6)quarter
從給定的日期/時間戳/字串中提取季度。
-- 4
select quarter("2020-12-30");
4. 日期、時間計算
1)months_between(end, start)
返回兩個日期之間的月數。引數1為截止時間,引數2為開始時間
-- 3.94959677
select months_between("1997-02-28 10:30:00", "1996-10-30");
2)add_months
返回某日期後n個月後的日期。
-- 2020-12-28
select add_months("2020-11-28", 1);
3)last_day(date)
返回某個時間的當月最後一天
-- 2020-12-31
select last_day("2020-12-01");
4)next_day(start_date, day_of_week)
返回某時間後the first date基於specified day of the week。
引數1:開始時間。
引數2:Mon、Tue、Wed、Thu、Fri、Sat、Sun。
-- 2020-12-07
select next_day("2020-12-01", "Mon");
5)date_add(start_date, num_days)
返回指定時間增加num_days天后的時間
-- 2020-12-02
select date_add("2020-12-01", 1);
6)datediff(endDate, startDate)
兩個日期相差的天數
-- 3
select datediff("2020-12-01", "2020-11-28");
7)關於UTC時間
-- to_utc_timestamp(timestamp, timezone) - Given a timestamp like '2017-07-14 02:40:00.0', interprets it as a time in the given time zone, and renders that time as a timestamp in UTC. For example, 'GMT+1' would yield '2017-07-14 01:40:00.0'.
select to_utc_timestamp("2020-12-01", "Asia/Seoul") ;
-- from_utc_timestamp(timestamp, timezone) - Given a timestamp like '2017-07-14 02:40:00.0', interprets it as a time in UTC, and renders that time as a timestamp in the given time zone. For example, 'GMT+1' would yield '2017-07-14 03:40:00.0'.
select from_utc_timestamp("2020-12-01", "Asia/Seoul");
常用的開窗函式
開窗函式格式通常滿足:
function_name([argument_list])
OVER (
[PARTITION BY partition_expression,…]
[ORDER BY sort_expression, … [ASC|DESC]])
function_name: 函式名稱,比如SUM()、AVG()
partition_expression:分割槽列
sort_expression:排序列
注意:以下舉例涉及的表employee中欄位含義:name(員工姓名)、dept_no(部門編號)、salary(工資)
1. cume_dist
如果按升序排列,則統計:小於等於當前值的行數/總行數(number of rows ≤ current row)/(total number of rows)。如果是降序排列,則統計:大於等於當前值的行數/總行數。用於累計統計。
舉例:
1)統計小於等於當前工資的人數佔總人數的比例 ,用於累計統計
SELECT
name,
dept_no,
salary,
cume_dist() OVER (ORDER BY salary) as cume
FROM employee;
2)按照部門統計小於等於當前工資的人數佔部門總人數的比例
SELECT
name,
dept_no,
salary,
cume_dist() OVER (PARTITION BY dept_no ORDER BY salary) as cume_val
FROM employee;
2. lead(value_expr[,offset[,default]])
用於統計視窗內往下第n行值。第一個引數為列名,第二個引數為往下第n行(可選,預設為1),第三個引數為預設值(當往下第n行為NULL時候,取預設值,如不指定,則為NULL)。
舉例:按照部門統計每個部門員工的工資以及大於等於該員工工資的下一個員工的工資
SELECT
name,
dept_no,
salary,
lead(salary, 1) OVER (PARTITION BY dept_no ORDER BY salary) as lead_val
FROM employee;
3. lag(value_expr[,offset[,default]])
與lead相反,用於統計視窗內往上第n行值。第一個引數為列名,第二個引數為往上第n行(可選,預設為1),第三個引數為預設值(當往上第n行為NULL時候,取預設值,如不指定,則為NULL)。
舉例:按照部門統計每個部門員工的工資以及小於等於該員工工資的上一個員工的工資
SELECT
name,
dept_no,
salary,
lag(salary, 1) OVER (PARTITION BY dept_no ORDER BY salary) as lag_val
FROM employee;
4. first_value
取分組內排序後,截止到當前行,第一個值。
舉例:按照部門統計每個部門員工工資以及該部門最低的員工工資
SELECT
name,
dept_no,
salary,
first_value(salary) OVER (PARTITION BY dept_no ORDER BY salary) as first_val
FROM employee;
5. last_value
取分組內排序後,截止到當前行,最後一個值。
舉例:按部門分組,統計每個部門員工工資以及該部門最高的員工工資
SELECT
name,
dept_no,
salary,
last_value(salary) OVER (PARTITION BY dept_no ORDER BY salary RANGE
BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) as last_val
FROM employee;
注意:
last_value預設的視窗是RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,表示當前行永遠是最後一個值,需改成RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING。
此外:
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW:為預設值,即當指定了ORDER BY從句,而省略了window從句 ,表示從開始到當前行(當前行永遠是最後一個值)。
RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING:表示從當前行到最後一行。
RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING:表示所有行。
n PRECEDING m FOLLOWING:表示視窗的範圍是[(當前行的行數)- n, (當前行的行數)+ m] row。
6. rank
對組中的資料進行排名,如果名次相同,則排名也相同,但是下一個名次的排名序號會出現不連續。比如查詢具體條件的topN行。RANK() 排序為 (1,2,2,4)。
7. dense_rank
dense_rank函式的功能與rank函式類似,dense_rank函式在生成序號時是連續的,而rank函式生成的序號有可能不連續。大資料培訓當出現名次相同時,則排名序號也相同。而下一個排名的序號與上一個排名序號是連續的。
DENSE_RANK() 排序為 (1,2,2,3)。
8. SUM/AVG/MIN/MAX
資料:
id time pv
1 2015-04-10 1
1 2015-04-11 3
1 2015-04-12 6
1 2015-04-13 3
1 2015-04-14 2
2 2015-05-15 8
2 2015-05-16 6
結果:
SELECT id,
time,
pv,
SUM(pv) OVER(PARTITION BY id ORDER BY time) AS pv1, -- 預設為從起點到當前行
SUM(pv) OVER(PARTITION BY id ORDER BY time ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS pv2, --從起點到當前行,結果同pv1
SUM(pv) OVER(PARTITION BY id) AS pv3, --分組內所有行
SUM(pv) OVER(PARTITION BY id ORDER BY time ROWS BETWEEN 3 PRECEDING AND CURRENT ROW) AS pv4, --當前行+往前3行
SUM(pv) OVER(PARTITION BY id ORDER BY time ROWS BETWEEN 3 PRECEDING AND 1 FOLLOWING) AS pv5, --當前行+往前3行+往後1行
SUM(pv) OVER(PARTITION BY id ORDER BY time ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS pv6 ---當前行+往後所有行
FROM data;
9. NTILE
NTILE(n),用於將分組資料按照順序切分成n片,返回當前切片值。
NTILE不支援ROWS BETWEEN,比如 NTILE(2) OVER(PARTITION BY cookieid ORDER BY createtime ROWS BETWEEN 3 PRECEDING AND CURRENT ROW)。
如果切片不均勻,預設增加第一個切片的分佈。
10. ROW_NUMBER
從1開始,按照順序,生成分組內記錄的序列。
比如,按照pv降序排列,生成分組內每天的pv名次
ROW_NUMBER() 的應用場景非常多,比如獲取分組內排序第一的記錄。
SparkSQL函式運算元以上函式都是可以直接在SQL中應用的。
那麼如果是在Spark SQL的DataFrame/DataSet的運算元中呼叫,可以參考DataFrame/DataSet的運算元以及org.apache.spark.sql.functions._下的函式: