hive SQL查询与函数
1. 查询语法
SELECT [ALL | DISTINCT] select_expr, select_expr, ...
FROM table_reference
[WHERE where_condition]
[GROUP BY col_list]
[HAVING col_list]
[ORDER BY col_list]
[CLUSTER BY col_list
| [DISTRIBUTE BY col_list] [SORT BY col_list]
]
[LIMIT [offset,] rows]
Hive中查询语法与MySQL中类似,关于以上建表语法解释如下:
- SELECT: SELECT 语句可以是union查询中的一部分,也可以是某个查询的子查询。
- ALL|DISTINCT:返回所有行或者去重后的行。支持 select distinct * 查询。
- FROM table_reference:表示查询的输入,可以是普通表、视图或者子查询。
- WHERE:查询条件,支持部分类型的子查询。
- GROUP BY :分组查询,指定分组列。
- HAVING:分组后对聚合结果过滤。
- ORDER BY|CLUSTER BY|DISTRIBUTE BY|SORT BY:排序相关操作。
- LIMIT:限制SELECT 语句返回的行数,接受一个或两个数字参数,它们都必须是非负整数常量。第一个参数指定要返回的第一行的偏移量,第二个参数指定要返回的最大行数。当给出单个参数时,它代表最大行数并且偏移量默认为 0。
注意:
1. 查询Hive表数据时,表和列名不区分大小写。
2. 使用Beeline或者DataGrip连接操作Hive时,后续可以使用Hive本地模式,可以通过“hive.exec.mode.local.auto”参数来开启,这样Hive查询就可以直接在HiveServer2本机执行,而不在集群上运行,在一些测试SQL场景中非常实用。
Hive启动后,默认使用JVM内存为256M,后续如果使用Hive本地模式运行SQL时,该内存较小会导致本地模式报错,最好将使用内存调大,可以通过HiveServer2节点上的$HIVE_HOME/conf/hive-env.sh文件中“HADOOP_HEAPSIZE”属性配置Hive使用内存:
#配置hive-env.sh HADOOP_HEAPSIZE参数
export HADOOP_HEAPSIZE=2048
2. groupby
Group By语句需要和聚合函数一起使用,表示按照组统计聚合,聚合操作包括COUNT/SUM/MIN/MAX/AVG/COUNT(DISTINCT...),select语句只能包含group by子句包含的列。
2.1 测试数据
#准备如下文件数据 data.txt
1,张三,男,25,手机,3000.00,2025-01-01
2,李四,女,30,电脑,7000.00,2025-01-01
3,王五,男,22,耳机,500.00,2025-01-02
4,赵六,女,28,手机,3200.00,2025-01-03
5,孙七,男,25,键盘,200.00,2025-01-03
6,周八,女,30,手机,3100.00,2025-01-04
7,吴九,男,25,鼠标,150.00,2025-01-04
8,郑十,女,30,鼠标,1200.00,2025-01-05
#创建 user_order表,并加载数据CREATE TABLE user_order(
userid INT,
name STRING,
gender STRING,
age INT,
product STRING,
amount DOUBLE,
purchase_date STRING
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ',';
load data inpath '/data.txt' into table user_order;
2.2 普通测试
set hive.exec.mode.local.auto=true;#按照性别统计用户数 SELECT gender, COUNT(*) AS cnt FROM user_order GROUP BY gender;
+---------+------+
| gender | cnt |
+---------+------+
| 女 | 4 |
| 男 | 4 |
+---------+------+SELECT gender, COUNT(DISTINCT product) AS dist_product, SUM(amount) AS total_amount FROM user_order GROUP BY gender;+---------+---------------+---------------+
| gender | dist_product | total_amount |
+---------+---------------+---------------+
| 女 | 3 | 14500.0 |
| 男 | 4 | 3850.0 |
+---------+---------------+---------------+
2.3 高级查询
GROUP BY 后也可以跟上ROLLUOP /CUBE/GROUPING SETS进行高级查询。
- ROLLUP:生成分组的层级汇总,包括总体汇总,适合需要分组和子分组汇总的场景。
#按照gender、age进行rollup查询
SELECT gender, age, SUM(amount) AS total_amount
FROM user_order
GROUP BY gender, age WITH ROLLUP;+---------+-------+---------------+
| gender | age | total_amount |
+---------+-------+---------------+
| 女 | 28 | 3200.0 |
| 女 | 30 | 11300.0 |
| 女 | NULL | 14500.0 |
| 男 | 22 | 500.0 |
| 男 | 25 | 3350.0 |
| 男 | NULL | 3850.0 |
| NULL | NULL | 18350.0 |
+---------+-------+---------------+
- CUBE:生成所有可能的分组组合及其汇总,适合需要全方位查看数据的场景。
# CUBE 相当于给分组列进行所有可能组合的分组得到结果
SELECT gender, age, SUM(amount) AS total_amount
FROM user_order
GROUP BY gender, age WITH CUBE;+---------+-------+--------------+
| gender | age | total_amount |
+---------+-------+--------------+
| 女 | 28 | 3200.0 |
| 女 | 30 | 11300.0 |
| 女 | NULL | 14500.0 |
| 男 | 22 | 500.0 |
| 男 | 25 | 3350.0 |
| 男 | NULL | 3850.0 |
| NULL | 22 | 500.0 |
| NULL | 25 | 3350.0 |
| NULL | 28 | 3200.0 |
| NULL | 30 | 11300.0 |
| NULL | NULL | 18350.0 |
+---------+-------+--------------+
- GROUPING SETS:指定特定的分组组合,适合自定义分组组合场景。
#只按照grouping sets中指定的组合进行聚合数据
SELECT gender, age, SUM(amount) AS total_amount
FROM user_order
GROUP BY gender, age
GROUPING SETS ((gender, age), (gender), ());+---------+-------+---------------+
| gender | age | total_amount |
+---------+-------+---------------+
| 女 | 28 | 3200.0 |
| 女 | 30 | 11300.0 |
| 女 | NULL | 14500.0 |
| 男 | 22 | 500.0 |
| 男 | 25 | 3350.0 |
| 男 | NULL | 3850.0 |
| NULL | NULL | 18350.0 |
+---------+-------+---------------
3. having
Having子句用于过滤“group by”聚合结果,Having与Where类似,但WHERE用于在分组之前过滤记录,而HAVING用于在分组之后过滤记录。使用HAVING可以基于聚合结果进行过滤。
案例:对user_order数据统计每个年龄消费金额,并保留消费金额大于500的年龄信息。
#统计每个年龄消费金额,保留消费金额大于500的年龄信息。
SELECT age, SUM(amount) AS total_amount
FROM user_order
GROUP BY age
HAVING SUM(amount) > 500;+------+---------------+
| age | total_amount |
+------+---------------+
| 25 | 3350.0 |
| 28 | 3200.0 |
| 30 | 11300.0 |
+------+---------------+
4. 排序
Hive中与排序相关的操作有Order By、Sort By、Distribute By、Cluster By 几种操作,下面分别进行介绍。
4.1 数据准备
#准备如下数据
1,zs,30,北京,100
2,ls,25,上海,200
3,ww,35,广州,300
4,zl,40,广州,400
5,cq,28,北京,500
6,gb,33,广州,600
7,wj,22,广州,700
8,zs,29,北京,800
9,ly,31,上海,900
10,se,27,上海,1000#Hive中创建user表,并将以上数据加载到表中
CREATE TABLE users (
user_id INT,
user_name STRING,
age INT,
city STRING,
salary INT
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ',';load data inpath '/data.txt' into table users;
4.2 Order BY
Order By用于对整个查询结果进行全局排序,HQL转换成MapReduce任务只有一个Reduce,可能会导致性能瓶颈问题。严格模式下(hive.exec.dynamic.partition.mode=strict)Order By必须与limit一起使用,防止单个reduce处理过多数据。非严格模式下(hive.exec.dynamic.partition.mode=nonstrict,默认)虽然不强制跟上limit,也建议和limit一起使用。
#使用order by 对工资进行全局倒序排序。
SELECT user_id,user_name,age,city,salary
FROM users
ORDER BY salary desc;+----------+------------+------+-------+---------+
| user_id | user_name | age | city | salary |
+----------+------------+------+-------+---------+
| 10 | se | 27 | 上海 | 1000 |
| 9 | ly | 31 | 上海 | 900 |
| 8 | zs | 29 | 北京 | 800 |
| 7 | wj | 22 | 广州 | 700 |
| 6 | gb | 33 | 广州 | 600 |
| 5 | cq | 28 | 北京 | 500 |
| 4 | zl | 40 | 广州 | 400 |
| 3 | ww | 35 | 广州 | 300 |
| 2 | ls | 25 | 上海 | 200 |
| 1 | zs | 30 | 北京 | 100 |
+----------+------------+------+-------+---------+
4.3 Sort By
Sort By可以对每个reduce的输出进行局部排序,数据在单个reduce中是有序地,即:每个reduce出来的数据时有序的,但全局可能不是有序的。在执行HQL时可以通过“set mapreduce.job.reduces=num;”来设置生成的MapReduce任务有几个Reduce。
#设置3个reduce
set mapreduce.job.reduces=3;#查询表users中数据并写入到HDFS文件中,按照age进行sortBy排序
INSERT OVERWRITE directory '/data/' row FORMAT DELIMITED FIELDS TERMINATED BY ','
SELECT user_id,user_name,age,city,salary from users sort by age desc;#查看3个文件中数据,按照sort by 指定的age列降序排序
[root@hadoop101 ~]# hdfs dfs -cat /data/000000_0
6,gb,33,广州,600
9,ly,31,上海,900
8,zs,29,北京,800
5,cq,28,北京,500
10,se,27,上海,1000
7,wj,22,广州,700[root@hadoop101 ~]# hdfs dfs -cat /data/000001_0
4,zl,40,广州,400
3,ww,35,广州,300
2,ls,25,上海,200[root@hadoop101 ~]# hdfs dfs -cat /data/000002_0
1,zs,30,北京,100
以上语句查询完成后,由于设置有3个Reduce,所以可以在HDFS相应目录中看到有3个文件生成:
4.4 Distribute By
Distribute By可以跟上某列,按照该列对数据进行分区,不保证分区内的数据有序。Distribute By类似MapReduce中的自定义分区Partition,将相同的key数据发送到同一个Reduce Task中处理。Distribute By的分区规则是按照分区字段的hash码与reduce的个数进行取模,余数相同的分到一个区。
#设置3个reduce
set mapreduce.job.reduces=3;#查询表users中数据并写入到HDFS文件中,按照city进行分区
INSERT OVERWRITE directory '/data/' row FORMAT DELIMITED FIELDS TERMINATED BY ',' SELECT user_id,user_name,age,city,salary from users distribute by city;#查看3个文件中数据city信息
[root@hadoop101 ~]# hdfs dfs -cat /data/000000_0
10,se,27,上海,1000
9,ly,31,上海,900
2,ls,25,上海,200[root@hadoop101 ~]# hdfs dfs -cat /data/000001_0[root@hadoop101 ~]# hdfs dfs -cat /data/000002_0
8,zs,29,北京,800
7,wj,22,广州,700
6,gb,33,广州,600
5,cq,28,北京,500
4,zl,40,广州,400
3,ww,35,广州,300
1,zs,30,北京,100
使用Distribute By时还可以跟上Sort By语句,指定每个分区中按照某列进行排序。以上数据我们发现相同城市数据去往了同一个文件,但是文件内的age是乱序的,所以可以在Distribute By后跟上Sort By指定按照age列进行排序。
#设置3个reduce
set mapreduce.job.reduces=3;#Distribute By + Sort By 一起使用
INSERT OVERWRITE directory '/data/' row FORMAT DELIMITED FIELDS TERMINATED BY ','
SELECT user_id,user_name,age,city,salary from users distribute by city sort by age desc;#以上执行完成后,查看每个文件中的数据,可以看到每个文件内的数据按照age降序排序
[root@hadoop101 ~]# hdfs dfs -cat /data/000000_0
9,ly,31,上海,900
10,se,27,上海,1000
2,ls,25,上海,200[root@hadoop101 ~]# hdfs dfs -cat /data/000001_0[root@hadoop101 ~]# hdfs dfs -cat /data/000002_0
4,zl,40,广州,400
3,ww,35,广州,300
6,gb,33,广州,600
1,zs,30,北京,100
8,zs,29,北京,800
5,cq,28,北京,500
7,wj,22,广州,700
4.5 Cluster By
当Distribute By和Sort By字段相同时,可以使用Cluster By 代替,但是排序只能是升序排序,不能指定排序为asc/desc。
#设置3个reduce
set mapreduce.job.reduces=3;#对users表中age字段进行Cluster By操作
INSERT OVERWRITE directory '/data/' row FORMAT DELIMITED FIELDS TERMINATED BY ','
SELECT user_id,user_name,age,city,salary from users cluster by age;#等价于
INSERT OVERWRITE directory '/data/' row FORMAT DELIMITED FIELDS TERMINATED BY ',' SELECT user_id,user_name,age,city,salary from users distribute by age sort by age;[root@hadoop101 ~]# hdfs dfs -cat /data/000000_0
10,se,27,上海,1000
8,zs,29,北京,800
1,zs,30,北京,100
9,ly,31,上海,900
4,zl,40,广州,400[root@hadoop101 ~]# hdfs dfs -cat /data/000001_0
6,gb,33,广州,600[root@hadoop101 ~]# hdfs dfs -cat /data/000002_0
7,wj,22,广州,700
2,ls,25,上海,200
5,cq,28,北京,500
3,ww,35,广州,300
4.6 总结
- Order By:全局排序,所有数据一个顺序,性能开销最大,适用于需要全局排序的情况。
- Sort By:局部排序,每个reducer内排序,适用于大数据集的部分排序。
- Distribute By:仅分区,不排序。
- Cluster By:分区内排序,每个分区的数据由一个reducer排序,适用于需要数据按键分区的场景。
- Order By和Sort By区别在于前者保证全局有序,后者仅保证Reducer内数据有序。
- Distribute By的分区规则是根据分区字段的hash码与reduce的个数进行相除后,余数相同的分到一个区。
- 如果Distribute By 、Sort By 排序字段一样,可以使用Cluster By 替代,即:Cluster By = Distribute By + Sort By,但Cluster By 排序只能是升序。
5. Hive Join
Hive中Join支持 [INNER] JOIN、LEFT [OUTER] JOIN、RIGHT [OUTER] JOIN、FULL [OUTER] JOIN、LEFT SEMI JOIN、CROSS JOIN ,下面通过一个案例来介绍LEFT SEMI JOIN、CROSS JOIN 。
LEFT SEMI JOIN - 左半开连接
LEFT SEMI JOIN 类似于 EXISTS 子查询,它返回左表中存在匹配行的行。右表只用于匹配,不包含在输出中。
#sql使用
SELECT *
FROM emp e
LEFT SEMI JOIN dept d ON e.dept_id = d.dept_id;#结果
+-----------+-------------+------------+
| e.emp_id | e.emp_name | e.dept_id |
+-----------+-------------+------------+
| 1 | 张三 | 001 |
| 2 | 李四 | 002 |
| 3 | 王五 | 003 |
| 4 | 马六 | 004 |
+-----------+-------------+--------------+
6. UNION/UNION ALL
Union用于将多个Select语句的结果合并为一个结果集,union默认会删除重复行,union all不会删除重复行。
#union使用,重复行数据自动去重
select emp_id,emp_name from emp where emp_id in (1,2,3,4)
union
select emp_id,emp_name from emp where emp_id in (1,2,3);+---------+-----------+
| emp_id | emp_name |
+---------+-----------+
| 1 | 张三 |
| 2 | 李四 |
| 3 | 王五 |
| 4 | 马六 |
+---------+-----------+#union all,相同数据不去重
select emp_id,emp_name from emp where emp_id in (1,2,3,4)
union all
select emp_id,emp_name from emp where emp_id in (1,2,3);+---------+-----------+
| emp_id | emp_name |
+---------+-----------+
| 1 | 张三 |
| 1 | 张三 |
| 2 | 李四 |
| 2 | 李四 |
| 3 | 王五 |
| 3 | 王五 |
| 4 | 马六 |
+---------+-----------+
7. hive视图
在Hive中如果经常使用到一个复杂SQL进行数据查询,我们可以对复杂SQL创建视图,这样就可以在后期使用中直接查询视图,比较方便。Hive中的视图与MySQL中的视图一样。
有两张表 student_info,student_score,对应数据及建表语句如下:
#student_info数据如下:
1 zs 18
2 ls 19
3 ww 20#student_info建表
create table student_info (id int,name string,age int) row format delimited fields terminated by '\t';
load data inpath '/student_info' into table student_info;#student_score数据如下:
1 zs 100
2 ls 200
3 ww 300#student_score建表
create table student_score (id int,name string,score int) row format delimited fields terminated by '\t';
load data inpath '/student_score' into table student_score;
假设我们经常需要使用如下结果:
select a.id,a.name,a.age,b.score from student_info a join student_score b on a.id = b.id
那么在Hive中经常使用以上查询,每次使用时都需要写一遍以上SQL执行,这里我们就可以直接创建Hive视图,方便后期查询。
Hive中创建视图语法如下:
CREATE VIEW [IF NOT EXISTS] [db_name.]view_name [(column_name [COMMENT column_comment], ...) ][COMMENT view_comment][TBLPROPERTIES (property_name = property_value, ...)]AS SELECT ... ;
我们可以针对以上场景创建视图“myview”:
#创建视图
create view myview as select a.id,a.name,a.age,b.score from student_info a join student_score b on a.id = b.id;#在后续使用中,直接使用视图myview即可
select * from myview;
在Hive中创建视图后,视图不是真正的表,不能加载数据操作,只是在Hive中保存了一份元数据,查询视图时执行对应的sql,视图不存储数据。
删除视图语法如下:
DROP VIEW [IF EXISTS] [db_name.]view_name;
删除创建的视图:
#我们可以将前面创建的视图删除
drop view myview;
8. 函数
Hive内置函数包括数学函数、集合函数、类型转换函数、日期函数、条件函数、字符串函数等。特别注意:Hive中所有的函数名称不区分大小写。
可以通过如下命令查询Hive中所有内置函数相关信息。
#查看Hive内置函数
show functions;#查看内置函数详细用法
desc function extended round;
+----------------------------------------------------+
| tab_name |
+----------------------------------------------------+
| round(x[, d]) - round x to d decimal places |
| Example: |
| > SELECT round(12.3456, 1) FROM src LIMIT 1; |
| 12.3' |
| Function class:org.apache.hadoop.hive.ql.udf.generic.GenericUDFRound |
| Function type:BUILTIN |
+----------------------------------------------------+
8.1 数学函数
返回类型 | 函数 | 说明 |
---|---|---|
BIGINT | round(double a) | 四舍五入 |
DOUBLE | round(double a,int d) | 小数部分d位之后数字四舍五入,例如round(21.263,2),返回21.26 |
BIGINT | floor(double a) | 对给定数据进行向下舍入最接近的整数。例如floor(21.8),返回21。 |
BIGINT | ceil(double a), ceiling(double a) | 将参数向上舍入为最接近的整数。例如ceil(21.2),返回22. |
double | rand(), rand(int seed) | 返回大于或等于0且小于1的平均分布随机数(依重新计算而变) |
double | exp(double a) | 返回e的n次方 |
double | ln(double a) | 返回给定数值的自然对数 |
double | log10(double a) | 返回给定数值的以10为底自然对数 |
double | log2(double a) | 返回给定数值的以2为底自然对数 |
double | log(double base, double a) | 返回给定底数及指数返回自然对数 |
double | pow(double a, double p) | 返回某数的乘幂 |
double | sqrt(double a) | 返回数值的平方根 |
string | bin(BIGINT a) | 返回二进制格式 |
string | hex(BIGINT a) hex(string a) | 将整数或字符转换为十六进制格式 |
string | unhex(string a) | 十六进制字符转换由数字表示的字符 |
string | conv(BIGINT num, int from_base,int to_base) | 将指定数值,由原来的度量体系转换为指定的体系。例如CONV('a',16,2),返回。参考:'1010' |
double | abs(double a) | 取绝对值 |
int double | pmod(int a, int b) | 返回a除b的余数的绝对值 |
double | sin(double a) | 返回给定角度的正弦值 |
double | asin(double a) | 返回x的反正弦,即是X。如果X是在-1到1的正弦值,返回NULL。 |
double | cos(double a) | 返回余弦 |
double | acos(double a) | 返回X的反余弦,即余弦是X,,如果-1<= A <= 1,否则返回null. |
int double | positive(int a) positive(double a) | 返回A的值,例如positive(2),返回2。 |
int double | negative(int a) negative(double a) | 返回A的相反数,例如negative(2),返回-2。 |
8.2 类型转换函数
返回类型 | 函数 | 说明 |
---|---|---|
指定的"type" | cast(expr as) | 类型转换。例如将字符”1″转换为整数:cast('1' as bigint),如果转换失败返回NULL。如果转换(expr为布尔值),Hive对非空字符串返回true。 |
8.3 集合函数
返回类型 | 函数 | 说明 |
---|---|---|
array | map_keys(Map) | 返回map的所有key |
array | map_values(Map) | 返回map的所有value |
int | size(Array(T)) | 返回数组类型的元素数量 |
int | size(Map) | 返回的map类型的元素的数量 |
boolean | array_contains(Array, value) | 如果数组包含value则返回TRUE |
array | sort_array(Array) | 根据数组元素的自然顺序按升序对输入数组进行排序并返回 |
8.4 日期函数
返回类型 | 函数 | 说明 |
---|---|---|
string | from_unixtime(bigint unixtime[,stringpattern]) | UNIX_TIMESTAMP参数为秒,表示返回一个值'yyyy-MM– dd HH:mm:ss'格式。该值表示在当前的时区。 |
bigint | unix_timestamp() | 获取以秒为单位的当前Unix时间戳(从'1970- 01–01 00:00:00'到现在的UTC秒数)为无符号整数。自2.0以来,已经被弃用了, 建议使用CURRENT_TIMESTAMP。 |
bigint | unix_timestamp(string date) | 指定日期参数调用UNIX_TIMESTAMP(date),它返回参数值'1970-01–01 00:00:00'到指定日期的秒数。 |
bigint | unix_timestamp(string date, string pattern) | 指定时间输入格式,返回'1970-01–01 00:00:00'到指定日期的秒数,:unix_timestamp('2024-07-24', 'yyyy-MM-dd') = .... |
string | to_date(string timestamp) | 返回时间中的年月日:to_date("1970-01-01 00:00:00") = "1970-01-01" |
int | year(string date) | 返回指定时间的年份,范围在1000到9999,例如:year("1970-01-01 00:00:00") = 1970, year("1970-01-01") = 1970 |
int | quarter(date/timestamp/string) | 返回1到4范围内的日期、时间戳或字符串的季度。例如:quarter('1970-04-08') = 2。 |
int | month(string date) | 返回指定时间的月份,范围为1至12月,例如:month("1970-11-01 00:00:00") = 11, month("1970-11-01") = 11。 |
int | day(string date) dayofmonth(date) | 返回指定时间的日期,例如:day("1970-11-01 00:00:00") = 1, day("1970-11-01") = 1。 |
int | hour(string date) | 返回指定时间的小时,范围为0到23。例如:hour('2009-07-30 12:58:59') = 12, hour('12:58:59') = 12。 |
int | minute(string date) | 返回指定时间的分钟,范围为0到59。 |
int | second(string date) | 返回指定时间的秒,范围为0到59。 |
int | weekofyear(string date) | 返回指定日期所在一年中的星期号,范围为0到53。 |
int | datediff(string enddate, string startdate) | 两个时间参数的日期之差。 |
int | date_add(string startdate, int days) | 给定时间,在此基础上加上指定的时间段。 |
int | date_sub(string startdate, int days) | 给定时间,在此基础上减去指定的时间段。 |
date | current_date | 返回当前日期。 |
timestamp | current_timestamp | 返回当前Unix时间戳。 |
string | date_format(date/timestamp/string ts, string pattern) | 使用指定的pattern将date/timestamp/string转换为字符串值。例如:date_format('1970-04-08','y') = '1970'。 |
8.5 条件函数
返回类型 | 函数 | 说明 |
---|---|---|
T | if(boolean testCondition, T valueTrue, T valueFalseOrNull) | 当testCondition为真时返回valueTrue,否则返回valueFalseOrNull |
boolean | isnull( a ) | 如果a为NULL返回true,否则返回false。 |
boolean | isnotnull ( a ) | 如果a不为NULL则返回true,否则返回false |
T | nvl(T value, T default_value) | 如果value为null则返回默认值,否则返回值 |
T | COALESCE(T v1, T v2, ...) | 返回第一个不为NULL的v,如果所有v都为NULL则返回NULL |
T | CASE a WHEN b THEN c [WHEN d THEN e]* [ELSE f] END | 当a = b,返回c;当a = d时,返回e;Else返回f |
T | CASE WHEN a THEN b [WHEN c THEN d]* [ELSE e] END | 当a为 true时,返回b;当c为true时,返回d;Else返回e |
T | nullif( a, b ) | 如果a=b,返回NULL;否则返回a |
8.6 字符串函数
返回类型 | 函数 | 说明 |
---|---|---|
string | concat(string A, string B…) | 连接多个字符串,合并为一个字符串,可以接受任意数量的输入字符串 |
string | concat_ws(string SEP, string A, string B…) | 链接多个字符串,字符串之间以指定的分隔符分开。 |
string | concat_ws(string SEP, array) | 使用指定的分隔符连接array中的元素。 |
int | find_in_set(string str, string strList) | 返回字符串str第一次在strlist出现的位置。如果任一参数为NULL,返回NULL;如果第一个参数包含逗号,返回0。 |
string | get_json_object(string json_string, string path) | 根据指定的json路径从json字符串中提取json对象,并返回提取的json对象的json字符串。如果输入的json字符串无效,它将返回null。注意:json路径只能包含字符[0-9a-z_],即不能包含大写或特殊字符。例如:select a.timestamp, get_json_object(a.appevents, '.eventname') from log a; |
int | length(string A) | 返回字符串的长度 |
string | lower(string A) lcase(string A) | 将文本字符串转换成字母全部小写形式 |
string | lpad(string str, int len, string pad) | 返回指定长度的字符串,给定字符串长度小于指定长度时,由指定字符从左侧填补。 |
string | rpad(string str, int len, string pad) | 返回指定长度的字符串,给定字符串长度小于指定长度时,由指定字符从右侧填补。 |
string | parse_url(string urlString, string partToExtract [, string keyToExtract]) | 返回URL指定的部分。parse_url('http://facebook.com/path1/p.php?k1=v1&k2=v2#Ref1', 'HOST')返回:'facebook.com' |
string | regexp_replace(string A, string B, string C) | 字符串A中的B字符被C字符替代 |
string | regexp_extract(string subject, string pattern, int index) | 通过下标返回正则表达式匹配的指定部分。regexp_extract('foothebar', 'foo(.*?)(bar)',2),匹配第二个捕获组所以返回'bar' |
string | repeat(string str, int n) | 重复N次字符串 |
string | replace(string A, string OLD, string NEW) | 返回字符串A,并将所有不重叠的OLD替换为NEW。示例:select replace("ababab","abab", "Z");返回"Zab"。 |
string | reverse(string A) | 返回倒序字符串 |
string | space(int n) | 返回指定数量的空格 |
array | split(string str, string pat) | 按照pat将字符串切分,转换为数组。 |
map | str_to_map(text[, delimiter1, delimiter2]) | 使用两个分隔符将文本分割为键值对。Delimiter1将文本分隔为K-V对,Delimiter2将每个K-V对分隔。delimiter1默认的分隔符是',';对于,delimiter2默认的分隔符是':'。 |
string | substr(string A, int start) substring(string A, int start) | 从文本字符串中指定的起始位置后的字符。 |
string | substr(string A, int start, int len) substring(string A, int start, int len) | 从文本字符串中指定的位置指定长度的字符。 |
string | trim(string A) | 删除字符串两端的空格,字符之间的空格保留 |
string | ltrim(string A) | 删除字符串左边的空格,其他的空格保留 |
string | rtrim(string A) | 删除字符串右边的空格,其他的空格保留 |
string | upper(string A) ucase(string A) | 将文本字符串转换成字母全部大写形式 |
8.7 数据掩码函数
数据掩码函数常用于数据脱敏。
返回类型 | 函数 | 说明 |
---|---|---|
string | mask(string str[, string upper[, string lower[, string number]]]) | 返回掩码版本的字符串。默认情况下,大写字母会转换为"X",小写字母会转换为 "x",数字会转换为 "n"。例如,mask("abcd-EFGH-8765-4321") 的结果是 xxxx-XXXX-nnnn-nnnn。你可以通过提供额外的参数来覆盖掩码中使用的字符:第二个参数控制大写字母的掩码字符,第三个参数控制小写字母的掩码字符,第四个参数控制数字的掩码字符。例如,mask("abcd-EFGH-8765-4321", "U", "l", "#") 的结果是 llll-UUUU-####-####。 |
string | mask_first_n(string str[, int n]) | 返回掩码版本的字符串,其中前n个字符被掩码。大写字母会转换为 "X",小写字母会转换为 "x",数字会转换为 "n"。例如,mask_first_n("1234-5678-8765-4321", 4) 的结果是 nnnn-5678-8765-4321。 |
string | mask_last_n(string str[, int n]) | 返回掩码版本的字符串,其中后n个字符被掩码。大写字母会转换为 "X",小写字母会转换为 "x",数字会转换为 "n"。例如,mask_last_n("1234-5678-8765-4321", 4) 的结果是 1234-5678-8765-nnnn。 |
string | mask_show_first_n(string str[, int n]) | 返回掩码版本的字符串,其中前n个字符不被掩码(从 Hive 2.1.0 开始支持)。大写字母会转换为 "X",小写字母会转换为 "x",数字会转换为 "n"。例如,mask_show_first_n("1234-5678-8765-4321", 4) 的结果是 1234-nnnn-nnnn-nnnn。 |
string | mask_show_last_n(string str[, int n]) | 返回掩码版本的字符串,其中后n个字符不被掩码。大写字母会转换为 "X",小写字母会转换为 "x",数字会转换为 "n"。例如,mask_show_last_n("1234-5678-8765-4321", 4) 的结果是nnnn-nnnn-nnnn-4321。 |
string | mask_hash(string | char |
8.8 复杂类型函数
Hive中常用复杂类型函数有如下几个:
函数 | 操作类型 | 说明 |
---|---|---|
map | (key1, value1, key2, value2, …) | 通过指定的键/值对,创建一个map。 |
struct | (val1, val2, val3, …) | 通过指定的字段值,创建一个结构。结构字段名称默认为COL1,COL2,... |
named_struct | (name1,val1,name2,val2,...) | 通过指定的字段值,创建一个结构。结构字段名称为指定的name1,name2...,name和value需要成对出现。 |
array | (val1, val2, …) | 通过指定的元素,创建一个数组。 |
复杂类型函数的操作符有如下几个:
运算符 | 操作类型 | 说明 |
---|---|---|
A[n] | A是一个数组,n为int型 | 返回数组A的第n个元素,第一个元素的索引为0。如果A数组为['foo','bar'],则A[0]返回'foo'和A[1]返回'bar'。示例:select likes[0] from person; |
M[key] | M是Map,key是其中的K。 | 返回关键值对应的值,例如map M为 {'f' -> 'foo', 'b' -> 'bar', 'all' -> 'foobar'},则M['all'] 返回'foobar'。示例:select address['beijing'] from person; |
S.x | S为struct | 返回结构x字符串在结构S中的存储位置。如 foobar {int foo, int bar} foobar.foo返回foo对应存储的整数。 |
9. Hive内置聚合函数(UDAF)
Hive中内置常用的聚合函数如下:
返回类型 | 函数 | 说明 |
---|---|---|
bigint | count(*) ,count(expr), count(DISTINCT expr[, expr., expr.]) | 返回记录条数。 |
double | sum(col),sum(DISTINCT col) | 求和 |
double | avg(col),avg(DISTINCT col) | 求平均值 |
double | min(col) | 返回指定列中最小值 |
double | max(col) | 返回指定列中最大值 |
double | percentile(BIGINTcol, p) | 返回数值区域的百分比数值点。0<=P<=1,否则返回NULL,不支持浮点型数值。 |
array | percentile(BIGINT col, array(p1,[,p2]…)) | 返回数值区域的一组百分比值分别对应的数值点。0<=P<=1,否则返回NULL,不支持浮点型数值。 |
double | var_pop(col) | 返回指定列的方差 |
double | var_samp(col) | 返回指定列的样本方差 |
double | stddev_pop(col) | 返回指定列的偏差 |
double | stddev_samp(col) | 返回指定列的样本偏差 |
double | covar_pop(col1, col2) | 两列数值协方差 |
double | covar_samp(col1, col2) | 两列数值样本协方差 |
double | corr(col1, col2) | 返回两列数值的相关系数 |
array | collect_set(col) | 返回无重复记录 |
array | collect_list(col) | 返回所有记录,可以包含重复的记录 |
10. Hive内置表生成函数(UDTF)
普通的用户自定义函数(如 concat())会接收一行输入并输出一行结果。而表生成函数(UDTF,User Defined TableGenerating Functions)则会将单行输入转换为多行输出。
返回类型 | 函数 | 说明 |
---|---|---|
T | explode(ARRAY a) | 将数组展开为多行。返回一个包含单列(col)的行集,数组中的每个元素对应一行。 |
Tkey,Tvalue | explode(MAP m) | 将映射展开为多行。返回一个包含两列(key和 value)的行集,输入映射中的每个键值对对应一行。 |
int,T | posexplode(ARRAY a) | 将数组展开为多行,并附加一个整数类型的位置信息列(表示元素在原数组中的位置,从0开始)。返回一个包含两列(pos 和 val)的行集,数组中的每个元素对应一行。 |
T1,...,Tn | inline(ARRAYSTRUCTf1:t1,...,fn:tn<> a) | 将结构体数组展开为多行。返回一个包含N列(N 是结构体中顶层元素的数量)的行集,数组中的每个结构体对应一行。 |
T1,...,Tn/r | stack(int r,T1 V1,...,Tn/r Vn) | 将n个值 V1,...,Vn 分解为 r 行,第一个参数是行数,后面的参数是要拆分的值,每行将包含n/r列。r 必须是常量。 |
string1,...,stringn | json_tuple(string jsonStr,string k1,...,string kn) | 接收一个JSON字符串和一组 n 个键,返回一个包含 n 个值的元组。相比于 get_json_object UDF,这个函数更高效,因为它可以通过一次调用获取多个键的值。 |
案例如下:
#explode演示
SELECT explode(array(1, 2, 3)) as col;
+------+
| col |
+------+
| 1 |
| 2 |
| 3 |
+------+SELECT explode(map('a', 1, 'b', 2)) as (key, value);
+------+--------+
| key | value |
+------+--------+
| a | 1 |
| b | 2 |
+------+--------+#posexplode演示
SELECT posexplode(array(1, 2, 3)) as (pos, val);
+------+------+
| pos | val |
+------+------+
| 0 | 1 |
| 1 | 2 |
| 2 | 3 |
+------+------+#inline演示
SELECT inline(array(named_struct('a', 1, 'b', 2), named_struct('a', 3, 'b', 4))) as (a, b);
+----+----+
| a | b |
+----+----+
| 1 | 2 |
| 3 | 4 |
+----+----+#stack演示
SELECT stack(2, 'a', 'b', 'c', 'd', 'e', 'f') as (col1, col2, col3);
+-------+-------+-------+
| col1 | col2 | col3 |
+-------+-------+-------+
| a | b | c |
| d | e | f |
+-------+-------+-------+#json_tuple演示
SELECT json_tuple('{"a":1, "b":2, "c":3}', 'a', 'b','c') as (a, b, c);
+----+----+----+
| a | b | c |
+----+----+----+
| 1 | 2 | 3 |
+----+----+----+
11. LATERAL VIEW
准备如下案例:
#创建表
CREATE TABLE lateral_tbl (name STRING,scores ARRAY<INT>
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t'
COLLECTION ITEMS TERMINATED BY ',';#向表中插入数据
insert into lateral_tbl values ('zs',array(1,2,3)),('ls',array(4,5,6));#查看表中数据
select * from lateral_tbl;
+-------------------+---------------------+
| lateral_tbl.name | lateral_tbl.scores |
+-------------------+---------------------+
| zs | [1,2,3] |
| ls | [4,5,6] |
+-------------------+---------------------+
针对以上表我们如果想要统计每个人对应的总得分,考虑可以先将scores列通过explode函数进行膨胀,然后按照name分组聚合。错误的SQL示例如下:
#如下SQL执行报错:UDTF's are not supported outside the SELECT clause, nor nested in expressions
select name,explode(scores) from lateral_tbl;
以上SQL在Hive中不支持,主要原因是使用UDTF查询过程中Select后只能包含单个UDTF,不能包含其他字段以及多个UDTF的情况,即:select中不能有额外的列与UDTF函数一起使用。
Lateral View 就可以解决以上问题,Lateral View 可以和explode等UDTF函数一起使用,可以将一列数据拆成多行,将拆分的结果组成一个支持别名的虚拟表,在SQL查询中可以查询当前虚拟表。lateral view使用语法如下:
LATERAL VIEW udtf(expression) tableAlias AS columnAlias (',' columnAlias)*
- tableAlias:虚拟表名称
- columnAlias:udtf函数返回的列,如果是多个使用括号包裹起来多个列,括号内多列使用逗号分割。
以上需求正确的SQL如下:
#explode膨胀后数据
select name,score from lateral_tbl lateral view explode(scores) new_view as score;
--结果:
+-------+--------+
| name | score |
+-------+--------+
| zs | 1 |
| zs | 2 |
| zs | 3 |
| ls | 4 |
| ls | 5 |
| ls | 6 |
+-------+--------+#最终聚合sql
select name,sum(score) from lateral_tbl lateral view explode(scores) new_view as score group by name;
--结果:
+-------+------+
| name | _c1 |
+-------+------+
| ls | 15 |
| zs | 6 |
+-------+------+