目录
0 引言
1 模板代码
2 代码分析及注意事项
hive是数据仓库、数据分析人员必用的工具之一。实际工作中,使用hive很多时候都是在将SQL代码封装在shell脚本之下运行,这是一种常见的方式,方便调度工具调度shell脚本。
#!/bin/bash
lastday=`date --date '-1days' +%Y-%m-%d` #获得昨天的日期
if [ "$2" != "" ];then
lastday=$2
fi;
input_para="hive" #默认启动方式
if [ "$1" != "" ];then
input_para=$1
fi;
#UDF函数使用形式
sqlFun1="CREATE TEMPORARY FUNCTION avgCalShock AS 'jttl.jxresearch.com.hive.udf.avgCalShock' using jar 'hdfs:///phm/JTTL_ETL_COMMON/jx-yjy-udfs-1.0-SNAPSHOT.jar'";
sqlFun2="CREATE TEMPORARY FUNCTION maxCalShock AS 'jttl.jxresearch.com.hive.udf.maxCalShock' using jar 'hdfs:///phm/JTTL_ETL_COMMON/jx-yjy-udfs-1.0-SNAPSHOT.jar'";
sqlFun3="CREATE TEMPORARY FUNCTION minCalShock AS 'jttl.jxresearch.com.hive.udf.minCalShock' using jar 'hdfs:///phm/JTTL_ETL_COMMON/jx-yjy-udfs-1.0-SNAPSHOT.jar'";
input_table="phmdwdb.dwd_iot_phm_switch_shock"; #输入表名
output_table="phmdwdb.dwd_phm_switch_shock_event"; #输出表名
log_dir=${output_table:8};
#Hive的连接方式参数获取(本方案采用oozie传参,也可以将参数放到hdfs上然后获取)
option=`echo ${input_para} | awk -F '_' '{print $1}' | sed s/[[:space:]]//g`
#SQL需要的参数获取
guoche_top_num=`echo ${input_para} | awk -F '_' '{print $3}' | sed s/[[:space:]]//g`
hive_home='/usr/idp/current/hive-client/bin';
sql="
insert overwrite TABLE $output_table
PARTITION (compute_day='$lastday') --注意sql中需要引用shell变量且为字符串时候,此处必须为单引号,使用双引号结果会显示不正确
select
XXX
,XXX
,XXX
from ${input_table}
where from_unixtime(cast(substr(msg_time,1,10) as bigint),'yyyy-MM-dd')='$lastday'
;
";
if [ "$option" = "beeline" ];then
# hive2地址获取
hive_addr=`hadoop fs -cat /phm/JTTL_ETL_COMMON/jdbc.properties | grep hive_addr | awk -F '=' '{print $2}' | sed s/[[:space:]]//g`
hive_url="${hive_addr}/phmdwdb"
cd $hive_home
beeline -u $hive_url -e "$sqlFun1;$sqlFun2;$sqlFun3;$sql" >>/tmp/$log_dir.log 2>&1 ;
fi
if [ "$option" = "hive" ];then
hive -e "$sqlFun1;$sqlFun2;$sqlFun3;$sql" >>/tmp/$log_dir.log 2>&1 ;
fi
#注意使用hive -e "$sql" 而不是hive -e $sql,$sql前必须有双引号。另外日志文件名必须和>>及$sql在同一行。
(1)hive -e方式
hive -e "待执行sql
"。这种方式允许我们在引号中写入需要执行的SQL语句。通常适合于语句较长的情况。这种方式也是在需要进行任务调度时采用的最直接方式,此时可以结合shell定义可变参数(如日期),再结合调度系统就可以实现脚本自动化。
hive -e遇到竖线分割时(特殊符号),要加多个转义符
先来看交互式命令行的方式。
假设我们要取出每个用户的城市和性别,使用split
函数,可能会采用以下写法:
select
split(location_city, '|')[0] as city,
split(location_city, '|')[1] as gender
from test_0102;
结果如下图所示。
显然结果不是我们想要的,这是因为竖线比较特殊。我们加上转义符再来看下。
select
split(location_city, '\|')[0] as city,
split(location_city, '\|')[1] as gender
from test_0102;
结果如下:
结果并未发生变化,不符合预期。如果再加一个转义符。
select
split(location_city, '\\|')[0] as city,
split(location_city, '\\|')[1] as gender
from test_0102;
结果如下:
综上实验可以看到我们最终的结果通过加两个//后 得到我们想要的结果。这是由于第一个转义符是从 hive -> MapReduce 过程的转义第二个转义符是 MapReduce 编译时的转义。
再来看使用hive -e的方式执行。如果直接用两个转义符,输出的结果仍然是把单字分隔开。
这是因为从shell到hive多了一步转义。因此需多加一个转义符。实际上,如果使用四个转义符,结果依然正确。具体规律直接给出:
令需要转义符的个数为 y
如何目标字符串包含'\',则 y = 2^n
如何目标字符串不包含'\',则 y = n
n:跨框架调用的次数,最终算到 java编译为止,一般最常见的就是上面两种情况。
参考链接:https://blog.csdn.net/lt793843439/article/details/91492088
相应的,如果遇到双竖线的情况,对每一个竖线则需要分别转义。例如我们要将上面数据中skills
一列分割出来。相应写法如下
hive命令行:每一个竖线两个转义符
hive -e:每一个竖线三个转义符(四个也行)
(2)hive -e 生成结果文件时,文件名要和重定向符放在一行
hive -e执行hiveSQL时,可以采用重定向符(>
)把查询结果写入文件。
hive -e"
use dac_twelve_dev;
select
split(location_city, '\\\|')[0] as city,
split(location_city, '\\\|')[1] as gender
from test_0102;" > test_0102.txt
cat test_0102.txt
北京 男
上海 女
北京 男
广州 女
西安 男
需要注意的是,结尾的双引号,重定向符号,结果文件文件名和要放在同一行。否则则可能不能如期生成结果文件。如下面几种方式所示。
#第一种
hive -e"
your SQL
" >
test_0102.txt
#第二种
hive -e"
your SQL"
>
test_0102.txt
#第三种
hive -e"
your SQL"
> test_0102.txt
上面的三种方式,第一种会报错:-bash: syntax error near unexpected token `newline
。第二种会在屏幕上打印结果后报相同的错,第三种会在屏幕上打印结果不报错,但最终结果文件没有数据。
(3)shell中执行hiveSQL打印SQL时注意星号
在调度中运行hiveSQL时,一般会使用shell脚本文件。脚本中先定义好时间变量,再定义SQL语句,最后使用hive -e方式执行SQL。类似于下面这样:
yesterday=`date -d "now -1 day" +%Y-%m-%d`
hql="
select * from xxt_able where ds='${yesterday}'
"
echo $hql#错误的写法,正确的是echo "$hql"
hive -e $hql > result.txt
这里需要注意的是如果定义的hql
语句中有*
号(等特殊符号),为了在echo打印时能够正常输出,以便于我们核查时间变量是否被正确替换。需要用"$hql"
而不要使用$hql
。否则在打印的时候,*
号会被当做shell的通配符,把当前路径下所有的文件名称都打印出来。同样也会hive在运行时报错。如下面代码和结果所示。*
在打印时被替换为了当前路径下所有的文件。
(4)关于hive执行时的其他选项
-S
选项屏蔽mapreduce日志
执行hiveSQL时,如果需要执行MapReduce过程,屏幕上会出现类似于map=100%,reduce=33%
这样的提示,如果任务比较复杂,日志长度也会相应增加。虽然能便于我们了解任务进度,但有时我们也会想把它屏蔽掉。使用hive -S -e "sql语句
"的方式,以Silent mode
运行hive,就可以实现这样的目,此时屏幕上只会有hive启动的日志,而不会有mapreduce过程的日志。
-v
选项打印出实际执行的SQL(调试SQL使用,一般会在shell脚本层使用sh -v +脚本名来调试)
这个选项可以用于前面提到的任务调度时,验证实际执行的详细SQL。假设我们提前定义好yesterday变量,-v
选项会将变量值打印出来,也就替代了echo "$hql"
的方式。(这里SQL报错了,我们为了演示变量,引用了表中不存在的ds
字段)
(5)SQL脚本中引用shell中的变量且为字符串时需要使用单引号。
1)Shell中单引号和双引号区别
#!/bin/bash
do_date=$1
echo '$do_date'
echo "$do_date"
echo "'$do_date'"
echo '"$do_date"'
echo ""$do_date""
echo ''$do_date''
echo `date`
[root@bigdata-1 dan_test]# ./test3.sh '2021-01-04'
$do_date
2021-01-04
'2021-01-04'
"$do_date"
2021-01-04
2021-01-04
2021年 01月 04日 星期一 20:39:41 CST
(1)单引号不取变量值,原样输出
(2)双引号取变量值。
(3)双引号内部嵌套单引号,取出变量值,变量值整体被取出后显示单引号会被作为字符串。
(4)单引号内部嵌套双引号,不取出变量值,将双引号内内容整体当作字符串输出
(5)双引号内嵌套双引号,取出变量值,但不显示双引号,整体输出不会作为字符串,双引号被抵消掉,相当于${变量}。
(6)单引号内嵌套单引号,取出变量值,但不显示单引号,整体输出不会作为字符串,单引号被抵消掉,相当于${变量}。
(7)反引号`,执行引号中命令
整体总结:双引号与单引号交替出现,看外层,如果外层是双引号则具备双引号取变量值的功能,且显示单引号。如果外层是单引号则原样输出。
双引号或单引号成对出现:此时无论单引号还是双引号将会被抵消掉,相当于${变量值}
双引号单引号交替出现,但双引号或单引号成偶数出现时,此时主要看外层,外层是双引号则抵消双引号,$里面的值会被解析,此时显示抵消掉双引号后的内容(不常用)
如果外层是单引号,则抵消的是单引号,$里面的值会被解析,此时显示抵消掉单引号后的内容(不常用)
#!/bin/bash
a=110
sql11=" " " '$a' " " "
echo $sql11
~
#!/bin/bash
a=110
sql11=' " " " '$a' " " " '
echo $sql11
#!/bin/bash
a=110
sql11=' " " "$a" " " '
echo $sql11
#!/bin/bash
a=110
sql11=" ' " " "$a" " " ' "
echo $sql11
对于非交替而是外层连续出现的则和成对出现情况一样
#!/bin/bash
a=110
sql11=' ' """"$a"""" ' '
echo $sql11
#!/bin/bash
a=110
sql11=' " '$a' " '
echo $sql11
#!/bin/bash
a=110
sql11=" ' "$a" ' "(会被用到)
echo $sql11
如下脚本:
2)SQL脚本中往往需要将带“-”的时间进行字符串形式输出,此时需要加单引号而不是双引号。
where中需要按天过滤必须要加引号否则会不识别,时间输出时候必须要加引号否则会输出乱码
参考链接:https://mp.weixin.qq.com/s/_18inMSkJKCBCCYWbDLJPw