(经常使用的两种建表例子)
SHOW FUNCTIONS;
[erp@master2-dev ~]$ hive -S -e "SHOW FUNCTIONS" | grep time
from_unixtime
from_utc_timestamp
to_unix_timestamp
to_utc_timestamp
unix_timestamp
[erp@master2-dev ~]$ hive -S -e "SHOW FUNCTIONS" | grep date
date_add
date_sub
datediff
finance.getdate
to_date
SHOW TABLES;
SHOW TABLES '*534';
DESCRIBE FUNCTION EXTENDED concat;
命令行中的Tab:列出全部关键字及补全。因此若是脚本须要在HIVE命令行里直接调试时,脚本不要使用Tab来缩进,使用空格
set hive.cli.print.header=true;
在命令行中,可使用 SET 命令显示或者修改变量的值
若是直接输入 SET 命令,即会显示全部环境变量
与 hivevar、 hiveconf 变量不一样的是,system: 与 env: 前缀是不能省略的
使用变量:${变量}
set hivevar:dd=aa;
select '${hivevar:dd}';//注:使用时加上命名空间
注:定义与使用时仍是最好加上hivevar名称空间,不然可能找不到
set hiveconf:hive.exec.dynamic.partition.mode=nonstrict;
show create table sap_r3_zfit534;
DESCRIBE formatted sap_r3_zfit534;
在hive命令提示符下执行Hadoop的dfs命令:
只须要将hadoop命令中的关键字hadoop去掉,并以分号结尾便可:
dfs -ls hdfs://SuningHadoop2/user/erp/hive/warehouse/erp.db/ztst_6;
用户在不用退出hive命令符就能够执行简单的 bash shell 命令:以 ! 开头,以 ; 结尾
hive> ! echo 'Hello';
NVL( string1, replace_with)
string1为 NULL则NVL函数返replace_with值,不然返string1值
droptable test;
CREATETABLE test AS
SELECT'a' f1,nullas f2,1 as f3
unionall
SELECT'b' f1,'c'as f2,1 as f3;
select *,nvl(f2,'值为NULL') from test;
concat(str1, str2, ... strN):若是其中任何一个为NULL,则结果为NULL,因此最好结合NVL使用:
concat(NVL(a.office,''),'00',NVL(b.posOrderId,'')))
droptable test;
CREATETABLE test AS
SELECT'a' f1,nullas f2,1 as f3
unionall
SELECT'b' f1,'c'as f2,1 as f3;
select *,concat(f1,f2,f3) from test;
select *,concat(f1,nvl(f2,'NULL'),f3) from test;
if(条件,值1,值2)
当条件为真时,取值1,不然取值2。值1或值2还能够是其余可返回值的函数表达式,如IF或CASE,便可以嵌套IF
droptable test;
CREATETABLE test AS
SELECT'a' f1,nullas f2,1 as f3
unionall
SELECT'b' f1,'c'as f2,1 as f3;
select *,if(f2 isnull,'NULL','NOT NULL') from test;
状况不少时,可使用CASE来代替嵌套的IF:
case
when b.kunnr is not null and b.kunnr <> '' then b.kunnr
when b.lifnr is not null and b.lifnr <> '' then b.lifnr
else b.hkont
end
droptable test;
CREATETABLE test AS
SELECT'a' f1,nullas f2,1 as f3
unionall
SELECT'b' f1,'c'as f2,2 as f3
unionall
SELECT'c' f1,'d'as f2,3 as f3;
select *,case f3 when 1 then'一' when 2 then'二' when 3 then'三'endfrom test;
去除先后空格
selecttrim(' facebook '),length(trim(' facebook '));
若是为NULL,则trim结果仍是NULL
droptable test;
CREATETABLE test AS
SELECT'a' f1,nullas f2,1 as f3
unionall
SELECT'b' f1,'c'as f2,2 as f3;
select *,trim(f2) from test;
注:Trim不能直接对NULL进行操做:
hive> selecttrim(null);
FAILED: SemanticException [Error 10014]: Line 1:7 Wrong arguments 'TOK_NULL': trim takes only STRING/CHAR/VARCHAR types. Found VOID
但经过其余函数返回的NULL值是能够的(由于这些函数返回的类型为字符类型而非VOID类型):
selecttrim(if(1<>1,'1',null));
substr(str, pos[, len]) :位置是从1开始数,不是0。若是是负,则从后往前数,而后再截后面
SELECTsubstr('Facebook', 5) ;
'book'
SELECTsubstr('Facebook', -5) ;
'ebook'
SELECTsubstr('Facebook', 5, 1);;
'b'
substring(a.XBLNR,1,4)--取XBLNR前四位
若是为NULL,则返回NULL:
字符串长度
若是为NULL,则返回NULL:
注:NULL <> 0
转大写:
upper(trim(nvl(c.USNAM,''))) <> 'RETAIL'
若是为NULL,则返回NULL:
若是有将NULL与空字符串都看作空的话,能够这样:
trim(nvl(b.KUNNR,'')) <> ''
左填充:lpad(str, len, pad)
若是str长度小于len,则使用pad填充左侧直到len长度:
SELECTlpad('hi', 5, '??') ;
若是str长度大于len,则会截断至len长度:
SELECTlpad('hi', 1, '??') ;
rpad(str, len, pad)
select from_unixtime(unix_timestamp(),'yyyy-MM-dd HH:mm:ss'); --当前时间
select from_unixtime(unix_timestamp('20150101','yyyyMMdd'),'yyy-MM-dd'); --格式化
select from_unixtime(unix_timestamp('2015/01-01','yyyy/MM-dd'),'yyyMMdd');--去掉日期格式
unix_timestamp('20150101','yyyyMMdd') 与 to_unix_timestamp('20150101','yyyyMMdd')相同
但 unix_timestamp 还能够返回当前时间,to_unix_timestamp不能够
hive 里(包括IF函数与Where条件里)判断是否为NULL要用 is null或 is not null ,不能使用 <> null 或 = null(虽然不报错)
测试以下:
droptable test;
CREATETABLE test AS
SELECT'a' f1,nullas f2,1 as f3
unionall
SELECT'b' f1,'c'as f2,1 as f3;
select * from test where f2 = null;
select * from test where f2 isnull;
select *,if(f2=null,'null','not null') from test;
select *,if(f2 isnull,'null','not null') from test;
droptable test1;
CREATETABLE test1 AS
SELECT'a' f1,nullas f2,1 as f3
unionall
SELECT'b' f1,'c'as f2,2 as f3;
droptable test2;
CREATETABLE test2 AS
SELECT'a' f1,nullas f2,3 as f3
unionall
SELECT'd' f1,'d'as f2,4 as f3;
select * from test1 a whereEXISTS(select f1 from test2 b where a.f1 = b.f1); --注:HIVE与标准SQL不一样的是:[NOT] EXISTS后面跟的子查询必定要是相关子查询,不然运行出错(相关子查询对外层查询结果集中的每条记录都会执行一次,因此尽可能少用相关子查询——标准SQL好似这样,HIVE不必定)
select * from test1 a where a.f1 IN(select f1 from test2 b);
select * from test1 a LEFT SEMI JOIN test2 b on a.f1 = b.f1;
SEMI-JOIN比一般的inner JION效率要高:对于左表中的一条记录,在右边表中一旦找到匹配的记录,Hive就会当即中止扫描
LEFT SEMI JOIN 的限制是:右表中的字段只能在ON 子句中设置过滤条件,在 WHERE 子句、SELECT 子句或其余地方过滤都不行,因此这也就是为何LEFT SEMI JOIN 只能看成 IN/EXISTS 来使用的缘由
早期版本可能还不支持[NOT] IN、[NOT] EXISTS,因此最好使用LEFT SEMI JOIN
ORDER BY为全局排序,会将全部数据送到同一个Reducer中后再对全部数据进行排序,对于大数据会很慢,谨慎使用
SORT BY为局部排序,只会在每个Reducer中对数据进行排序,在每一个Reducer输出是有序的,但并不是全局排序(每一个reducer出来的数据是有序的,可是不能保证全部的数据是有序的——即文件(分区)之间无序,除非只有一个reducer)
DISTRIBUTE BY 是控制map的输出被送到哪一个reducer端进行汇总计算。注:HIVE reducer分区个数由mapreduce.job.reduces来决定,该选项只决定使用哪些字段作为分区依据,若是没经过DISTRIBUTE BY指定分区字段,则默认将整个文本行作为分区依据。分区算法默认是HASH,也能够本身实现。
注:这里DISTRIBUTE BY讲的分区概念是指Hadoop里的,而非咱们HIVE数据文本存储分区。Hadoop里的Partition主要做用就是将map的结果发送到相应的reduce,默认使用HASH算法,不过能够重写
droptable test1;
createtable test1 as
select'typea' f1,6 f2
unionall
select'typea' f1,2 f2
unionall
select'typeb' f1,7 f2
unionall
select'typec' f1,8 f2
unionall
select'typeb' f1,5 f2
unionall
select'typea' f1,1 f2
unionall
select'typeb' f1,3 f2
unionall
select'typec' f1,4 f2;
select * from test1 orderby f2 asc;--全局有序
set mapreduce.job.reduces=10;
select * from test1 sort by f2;--虽然SORT BY是区内有序,但因为未经过DISTRIBUTE指定分区字段,而最大分区又设置为了10,因此每条记录所分配到的reducer可能不尽相同(有可能某两条会放在同一分区中,这取决于HASH算法),因此此时看不出什么区内有序
set mapreduce.job.reduces=1;
select * from test1 sort by f2;--将最大分区设置为一个分区,因此具备order by同样具备全局排序效果
set mapreduce.job.reduces=10;
select * from test1 DISTRIBUTE BY f1 sort by f2;--将最大分区设为10,再经过DISTRIBUTE指定分区字段,而不使用默认整行文原本分区
set mapreduce.job.reduces=2;
select * from test1 DISTRIBUTE BY f1 sort by f2;--因为分区最大设置为2,f1分区字段值有3种,这会根据HASH分区算法,会将其中某两种放在同一分区,而另一种放在另外的分区,最终看到两个分区内部也是有序的
相似Oracle中的ROWNUM,给查询出的记录编号,HIVE中通常与DISTRIBUTE BY一块儿使用。其做用按指定的列进行分组生成行序列,在ROW_NUMBER() 时,会根据 DISTRIBUTE BY (a,b...)中指定的列来判断,若两条记录的a,b列相同,则行序列+1,不然从新计数。由于HIVE是基于MAPREADUCE的,必须保证列值相同的记录要在同一个reduce中,因此须要与DISTRIBUTE BY结合使用,不然ROW_NUMBER无心义。
假设一个场景:存在表test1,该表的数据以下
id rate score
1 '0-4' 10
2 '0-4' 40
3 '0-4' 30
4 '0-4' 20
5 '5-10' 10
6 '5-10' 40
7 '5-10' 30
8 '5-10' 20
9 '11-20' 10
10 '11-20' 40
11 '11-20' 30
12 '11-20' 20
如今要求用一条查询语句取出每种rate下score最大的两条记录,也就算取出id为:2,3,6,7,10,11的记录
droptable test1;
createtable test1 as
select 1 id,'0-4' rate,10 score
unionall
select 2 id,'0-4' rate,40 score
unionall
select 3 id,'0-4' rate,30 score
unionall
select 4 id,'0-4' rate,20 score
unionall
select 5 id,'5-10' rate,10 score
unionall
select 6 id,'5-10' rate,40 score
unionall
select 7 id,'5-10' rate,30 score
unionall
select 8 id,'5-10' rate,20 score
unionall
select 9 id,'11-20' rate,10 score
unionall
select 10 id,'11-20' rate,40 score
unionall
select 11 id,'11-20' rate,30 score
unionall
select 12 id,'11-20' rate,20 score;
SELECT a.* FROM(SELECT *,row_number() over (distribute by rate SORTBY rate ASC, score DESC) rownum FROM test1 ) a WHERE rownum <= 2 ;--结果正确,只启动一个JOB
注:能够去掉SORT BY后面分区字段,而不影响结果,应该是在排序时默认就已加上了分区字段,但以防出错,不要省略
注:ROW_NUMBER+DISTRIBUTE BY结果与分区个数无关,因此经过set mapreduce.job.reduces不会影响正确结果:
hive> set mapreduce.job.reduces=1;
hive> SELECT a.* FROM(SELECT *,row_number() over (distribute by rate SORT BY score DESC) rownum FROM test1 ) a WHERE rownum <= 20;
SELECT a.* FROM(SELECT *,row_number() over (partition by rate ) rownum FROM test1 SORT BY rate ASC, score DESC) a WHERE rownum <= 2 ;--看见有人这么用过,但结果不正确,partition到底有啥用?会启动两个JOB,相对于distribute感受慢,因此仍是使用distribute吧
row_number()另外一做用能够用来去除重复:先按分组字段分区,再经过 rownum = 1过滤便可。另外,去重还能够借助于group by:
select actual_pymnt_dt from sap_r3_ZFIT684_tmp groupby actual_pymnt_dt
为了提交性能,INNER JOIN时,非链接条件放置的位置应该按照 ON > WHERE > HAVING的顺序优先放置,由于SQL条件的的执行通常是按这个顺序来执行的,将条件放在最开始执行,则可过滤掉大部数据;
若是是LEFT JOIN,非链接条件放在WHERE仍是ON中是有所不一样的,请参考后面
一、 ON条件中不支持OR链接,只能使用AND
二、 在外链接中,不要轻易的将Where中的条件移到ON链接语句中(虽然不报错),由于在HIVE的外链接ON语句中,会忽略(严格来说不是忽略,而是只拿知足条件的记录去与另外一表进行关联,左表没关联上的仍是会显示出来,请看后面实验)掉全部除链接字段条件全部条件:
droptable test1;
CREATETABLE test1 AS
SELECT'a' f1,nullas f2,1 as f3
unionall
SELECT'b' f1,'c'as f2,2 as f3;
droptable test2;
CREATETABLE test2 AS
SELECT'a' f1,nullas f2,3 as f3
unionall
SELECT'd' f1,'d'as f2,4 as f3;
select * from test1 a leftjoin test2 b on a.f1=b.f1;
select * from test1 a leftjoin test2 b on a.f1=b.f1 and a.f1='a';
select * from test1 a leftjoin test2 b on a.f1=b.f1 where a.f1='a';
严格为讲,ON中的非链接条件仍是起必定做用的:以下面的a记录所对应的右表记录为NULL,由于条件a.f1='b'只会拿知足条件的左表记录b去与右表去关联,但未关联上,因此对应的右表显示为NULL;不知足条件的左表记录a不会去作关联(虽然在右表中存在),但仍是会显示出来,只是因此对应的右表也显示为NULL:
select * from test1 a leftjoin test2 b on a.f1 = b.f1 and a.f1 = 'b';
思考下面的结果?
select * from test1 a leftjoin test2 b on a.f1=b.f1 and b.f1='d';
对于INNER JOIN,ON语中中的非链接条件是起作用的:
select * from test1 a innerjoin test2 b on a.f1=b.f1 and a.f1='a';
总结:
一、 若是是INNER JOIN,为了提升性能,非链接字段条件最好放置在ON从句中
二、 若是是LEFT JOIN,非链接字段条件最好放在Where从句中,但若想放在ON从句中,可使用嵌套子查询来解决没必要要的麻烦:
在外联连中,若是要想Where语句中的条件移到ON语句中,可使用以下的嵌套语句来实现,这样即在联接前过滤了没必要要的数据,提升效率的同时又没有丢掉数据:
select * from (select * from test1 where f1='a') a leftjoin test2 b on a.f1=b.f1;
若是某字段存为NULL的值,则用该字段进行过滤时,NULL须要单独处理:
droptable test1;
CREATETABLE test1 AS
SELECT'a' f1,nullas f2,1 as f3
unionall
SELECT'b' f1,'c'as f2,2 as f3
unionall
SELECT'd' f1,'d'as f2,3 as f3;
select * from test1 where f2 <> 'c';--NULL值的没有查出来(标准SQL也是这样的)
select * from test1 where f2 <> 'c' or f2 is null; --若是须要取出NULL,须要单独加上
regexp_extract(str, regexp[, idx]) - extracts a group that matches regexp抽取匹配到的指定组
SELECT regexp_extract('100\\200', '^(\\d+).(\\d+)$', 0);
100\200
SELECT regexp_extract('100-200', '^([0-9]+)-([0-9]+)$', 1);
100
SELECT regexp_extract('100-200', '^([0-9]+)-(\\d+)$', 2);
200
注:\须要使用转义一下
不要使用Float类型
在建表时,若是要将金额字段定义成数据类型,请将定义成Double类型,或对数字类型比较时,请先转换成Double再进行比较,不然不许确(早其版本会有精度丢失问题):
cast(a.payAmount as double) = cast(b.payAmount as double)
或者直接经过字符串比较的方式来比较数字,但比较前须要先后对齐(如不补齐会致使9.8 > 10.8),请看下面:
select a as a000000000,
regexp_extract(trim(a),'^([-+]?)([0-9]*)(\.?)([0-9]*)$',0) c0,--整个匹配
regexp_extract(trim(a),'^([-+]?)([0-9]*)(\.?)([0-9]*)$',1) c1,--正负号
regexp_extract(trim(a),'^([-+]?)([0-9]*)(\.?)([0-9]*)$',2) c2,--整数部分
regexp_extract(trim(a),'^([-+]?)([0-9]*)(\.?)([0-9]*)$',3) c3,--小数点
regexp_extract(trim(a),'^([-+]?)([0-9]*)(\.?)([0-9]*)$',4) c4,--小数部分
case concat(regexp_extract(trim(a),'^([-+]?)([0-9]*)(\.?)([0-9]*)$',1),'')
when '-'then
concat('-',lpad(regexp_extract(trim(a),'^([-+]?)([0-9]*)(\.?)([0-9]*)$',2),16,'0'),'.',
rpad(regexp_extract(trim(a),'^([-+]?)([0-9]*)(\.?)([0-9]*)$',4),4,'0'))
else
concat('0',lpad(regexp_extract(trim(a),'^([-+]?)([0-9]*)(\.?)([0-9]*)$',2),16,'0'),'.',
rpad(regexp_extract(trim(a),'^([-+]?)([0-9]*)(\.?)([0-9]*)$',4),4,'0'))
end c500000000000000000000,--整数、小数部分对齐
case concat(regexp_extract(trim(a),'^([-+]?)([0-9]*)(\.?)([0-9]*)$',1),'')
when '-'then
cast(concat('-',lpad(regexp_extract(trim(a),'^([-+]?)([0-9]*)(\.?)([0-9]*)$',2),16,'0'),'.',
rpad(regexp_extract(trim(a),'^([-+]?)([0-9]*)(\.?)([0-9]*)$',4),4,'0')) asdouble)
else
cast(concat('0',lpad(regexp_extract(trim(a),'^([-+]?)([0-9]*)(\.?)([0-9]*)$',2),16,'0'),'.',
rpad(regexp_extract(trim(a),'^([-+]?)([0-9]*)(\.?)([0-9]*)$',4),4,'0')) asdouble)
end c6--转换成真正的小数
from test0002;
insert overwrite table erp.tsor_BKPF --要更新此表
SELECT td.* FROM (
select ta.*
from erp.tsor_BKPF ta --先要把原来中未更新的数据捞出来
leftjoin (select tc.MANDT,tc.BUKRS,tc.BELNR,tc.GJAHR from BI_SOR.TSOR_FN_R3_BKPF_D tc --此表为增量表
where tc.STATIS_DATE='${hivevar:statis_date}' )tb on
ta.MANDT=tb.MANDT
and ta.BUKRS=tb.BUKRS
and ta.BELNR=tb.BELNR
and ta.GJAHR=tb.GJAHR
where concat(tb.MANDT,tb.BUKRS,tb.BELNR,tb.GJAHR) isnull
union all
select ta.* --再与发生更新的数据Union
from BI_SOR.TSOR_FN_R3_BKPF_D ta
where ta.STATIS_DATE='${hivevar:statis_date}'
)td;
分区表更新:
insert overwrite table erp.sap_r3_ZFIT684 PARTITION (actual_pymnt_dt)
select e.* from
(
--未更新的数据
select a.* from (
select * from erp.sap_r3_ZFIT684 d --目标表
--CBT平台抛数据任务里不支持 HIVE变量,因此若是要使用 ${hivevar:statis_date}变量的话,须要将后置SQL作成HIVE任务
--where actual_pymnt_dt = '${hivevar:statis_date}'
LEFT SEMI JOIN (select actual_pymnt_dt from sap_r3_ZFIT684_tmp groupby actual_pymnt_dt) dd
ond.actual_pymnt_dt = dd.actual_pymnt_dt--从目标表中只捞出须要处理的分区数据
) a
leftjoin (select
mandt,serial_no,shkzg,xblnr,payee_co_code,biz_categ,biz_sub_categ,zuonr,money,pymnt_amt,
supplier_name,waers,twaers,sgtxt,bukrs3,belnr3,gjahr3,flag3,meg3,belnr2,gjahr2,flag2,meg2,
belnr1,gjahr1,budat1,flag1,meg1,msg,clear,refund_no,file_name,RECIVE_DT,RECIVE_TM,actual_pymnt_dt
from erp.sap_r3_ZFIT684_tmp) b --增量表
on a.MANDT=b.MANDT and a.SERIAL_NO=b.SERIAL_NO and a.SHKZG=b.SHKZG --经过主键进行关联
where b.MANDT isnulland b.SERIAL_NO isnulland b.SHKZG isnull
union all
--已更新的数据(包括新增、修改的数据,删除须要在上面取未更新的数据时过滤掉便可 )
select
mandt,serial_no,shkzg,xblnr,payee_co_code,biz_categ,biz_sub_categ,zuonr,money,pymnt_amt,
supplier_name,waers,twaers,sgtxt,bukrs3,belnr3,gjahr3,flag3,meg3,belnr2,gjahr2,flag2,meg2,
belnr1,gjahr1,budat1,flag1,meg1,msg,clear,refund_no,file_name,RECIVE_DT,RECIVE_TM,actual_pymnt_dt
from erp.sap_r3_ZFIT684_tmp c
) e;
一、 JOIN查询时,尽可能将小表放在前面
二、 两个表join的时候,不支持两个表的字段非等值操做,能够将非相等条件提取到where中