oracle (+)学习_cclovezbf的博客-CSDN博客最近工作需要将oracle的存储过程转化为hive的sql脚本。遇到很多不一样的地方,例如oracle连接中有(+)号的用法。借鉴这篇文章,但是这个排版比较烂。。。先建表和插入数据首先说明(+)代表什么?代表这一侧的数据可以为空!a.id=b.id(+) 代表b表和a表关联的时候以a表作为主表。https://blog.csdn.net/cclovezbf/article/details/128305437
例如select a.id, (select b.id from b where b.name=a.id) from a
hive 是不支持select 里面子查询的。 修改如下
select a.id ,b.id from a left join b on a.id=b.name
decode('key',if1,then1 ,if2,then2...thenN)
一般来说可以改为case key when if1 then then1 when if2 then then2 else thenN
但是如果decode比较简单 可以直接改为 if('key'=if1,then1,then2)
复杂的改为case when 。特别注意hive有个decode函数是编码函数
例如某字符串yyyyMM获取上个月时间
oracle
select to_char(add_months(to_date('202202','yyyymm'),-1),'yyyymm') from dual
hive
select date_format(add_months(to_date(from_unixtime(unix_timestamp('202212','yyyyMM'))) ,-1),'yyyMM');
其实可以简化
select add_months(from_unixtime(unix_timestamp('202212','yyyyMM')),-1,'yyyMM');
select to_char(sysdate ,'YYYY-MM-DD') from dual
hive的
select to_date(current_timestamp); --这里返回值是date!!!!
如果hive要转化为其他格式 可以用date_format(current_date,'yyyy-MM-dd HH:mm:ss')
这里要注意oracle对格式要求比较严格,hive则不需要特别严格。
我们这边为了图方便把hive的日期格式都拿string去存,所以会有些些出入
例如oracle
SELECT 'yyyyMM', to_char (SYSDATE,'yyyyMM') FROM dual UNION ALL
SELECT 'yyyy-MM', to_char (SYSDATE,'yyyy-MM') FROM dual UNION ALL
SELECT 'yyyy-MM-dd', to_char (SYSDATE,'yyyy-MM-dd') FROM dual UNION ALL
SELECT 'yyyy/MM/dd', to_char (SYSDATE,'yyyy/MM/dd') FROM dualoracle的to_char 函数非常方便只要你的入参1是日期类型 就可以想要什么格式要什么格式
如果我们hive存的是日期格式也还好
hive的date类型格式化
SELECT 'yyyyMM', date_format (current_date(),'yyyyMM') UNION ALL
SELECT 'yyyy-MM', date_format (current_date,'yyyy-MM') UNION ALL
SELECT 'yyyy-MM-dd', date_format (current_date,'yyyy-MM-dd') UNION ALL
SELECT 'yyyy/MM/dd', date_format (current_date,'yyyy/MM/dd')
hive的string 类型格式。 如果你存标准日期格式例如 2022-12-13 14:15:16 可以直接按照上面写。
但是如果你存的是20221213 2022/12/13这种格式。(当然你可以substring+ concat("-")拼)。
说点其他的。
可以通过如下方法格式,我们只需要修改format对应hive表里用string存储的日期格式即可
select from_unixtime(UNIX_TIMESTAMP('20200102','yyyyMMdd')--2020-01-02 00:00:00
select to_date(from_unixtime(UNIX_TIMESTAMP('20200102','yyyyMMdd'))) -- 2020-01-02
说个简单且经常遇到的的转化
oracle ->TO_CHAR(TO_DATE(REPLACE(ADJ.VAR1, '-',''), 'YYYYMMDD'), 'yyyy')
hive -> from_unixtime(UNIX_TIMESTAMP(REPLACE(ADJ.VAR1, '-',''), 'YYYYMMDD'), 'yyyy')
https://blog.csdn.net/cclovezbf/article/details/128326389https://blog.csdn.net/cclovezbf/article/details/128326389
参考文章
Oracle中的instr()函数 详解及应用_Java&Develop的博客-CSDN博客_oracle中instr
SELECT instr('1234567890123456789','3') FROM dual -- 3
SELECT instr('1234567890123456789','3',1) FROM dual -- 3 ,从第1位开始查找第一个3
SELECT instr('1234567890123456789','3',1,2) FROM dual --13 从第1位开始查找第二个3
SELECT instr('1234567890123456789','3',4) FROM dual -- 13 从第4位开始查找第一个3
SELECT instr('1234567890123456789','3',4,1) FROM dual --13 从第4位开始查找第一个3
SELECT instr('1234567890123456789','3',4,2) FROM dual --0 从第4位开始查找第二个3
select instr('被查找的字符串','我们需要查找的字符',从第几位开始 首位是0,查找第几个出现的)
注意这里返回的下标是从1开始的。
接着我们来看下hive的函数 有几个类似的 instr
instr(str, substr) - Returns the index of the first occurance of substr in str
SELECT instr('1234567890123456789','3') -- 3 没问题 但是这个功能比较简单,远没有oracle的强大,不过也凑合用了。
hive-locate函数
select locate('3','12345123',4) --8
select locate('3','12345123',1) --3-- 这个locate函数也是找到字符串的下标 locate('要找的字符','被找的字符串',' 从下标多少开始找')。
说实话 感觉这个函数也没有oracle instr的函数好用,因为 instr(str,substr, count) 可以找第几次出现 对于 xx.xx.xx 和x.x.x 这种格式的数据特别好用
说个遇到的案例 oracle中 需要截取某个字段
SELECT
SEGMENT_NAME_MERGE,
SUBSTR(T.SEGMENT_NAME_MERGE,
INSTR(T.SEGMENT_NAME_MERGE, '.', 1, 2) + 1,
(INSTR(T.SEGMENT_NAME_MERGE, '.', 1, 3) - INSTR(T.SEGMENT_NAME_MERGE, '.', 1, 2)) - 1
)
FROM ODSERPDATA.ODS_CE_GL_ACCOUNT_Q T
字段格式是: xx1.xxxxx2.xx3.xxx4.xx5.xx6 我们需要xx3格式的数据
缺省.受限制现金-人民币-风险准备金专户.工行高新支行321413RMB(财付通专用).缺省.缺省.缺省.缺省.缺省 -> 工行高新支行321413RMB(财付通专用)
之前的人采用了substring('字符串',第二个.字符的下标+1, (第三个. - 第二个.的长度 + 1))。
现在我们要在hive中也截取成这样。 我们能一样采用instr +substr吗?
不能!!! 因为hive的instr只能定位到第一个. 那我们怎么办?
这里我们可以使用substring_index 或regexp_extract
select replace(substring_index(a,'.',3),substring_index(a,'.',2)||'.',''), substr(a,length(substring_index(a,'.',2))+2,length(substring_index(a,'.',3))-length(substring_index(a,'.',2))-1), regexp_extract(a,'.*?\\..*?\\.(.*?)\\.+',1) from (select '缺省.受限制现金-人民币-风险准备金专户.工行高新支行321413RMB(财付通专用).缺省.缺省.缺省.缺省.缺省' a )t 上面是三种办法。 1.是替换 2.是截取 3是正则
oracle 这样是可以的
SELECT * FROM (SELECT 1,2 FROM dual )
hive注意必须临时表名
select * from (select 1, 2 )t --正确
select * from (select 1, 2 ) --错误
oracle
INSERT INTO TEST.CC_STUDENT_02
WITH tmp AS (SELECT * FROM TEST.CC_STUDENT_02 cs )
SELECT * FROM tmp
hive 顺序有点不同
WITH tmp AS (SELECT * FROM TEST.CC_STUDENT_02 cs )
INSERT INTO TEST.CC_STUDENT_02
SELECT * FROM tmp
oracle
SELECT substr('202212', 1, 4) - 1 || 'aa' FROM dual -- 2021aa
hive
SELECT substr('202212', 1, 4) - 1 || 'aa' -- 2021.0aa
解决办法
SELECT cast(substr('202212', 1, 4) - 1 as int)|| 'aa' -- 2021aa
SELECT cast(substr('202212', 1, 4) as int) - 1|| 'aa' -- 2021aa
因为int-int=int 。 string-int 和int-string=double
这是因为hive 在'2022'-1的时候计算结果是double类型 double+string 保留了一位小数
oracle
SELECT 1||NULL||2 FROM dual -- 12
hive
SELECT 1||NULL||2 --null
解决办法
select 1||nvl(null,'')||2
——————————————————————————————————————————
最近遇到了一个特别搞人的oracle时间转化。就是oracle有个string字段里面的日期格式非常不标准有 - / 然后01省略0的,但是通过to_date函数都可以转化
不得不说oracle的to_date函数非常强大可以转化不标准的时间字符串为date类型
SELECT to_date('2017-3-31', 'yyyy/mm/dd') FROM dual UNION ALL
SELECT to_date('2018/11/6', 'yyyy/mm/dd') FROM dual UNION ALL
SELECT to_date('2017/6/20', 'yyyy/mm/dd') FROM dual UNION ALL
SELECT to_date('2017-06-20', 'yyyy/mm/dd') FROM dual UNION ALL
SELECT to_date('20170620', 'yyyy/mm/dd') FROM dual
hive呢? hive怎么搞? var9就是原列,trade_date就是转化后的。案例中的20170620是不存在的(我自己造的)。。
此时我已经想到了一个复杂的办法。。。
说下思路
1.自己定义一个udf函数,用java的方式去判断然后补充0 成为标准的时间字符串
2.我想到了oracle的instr函数可以获取的第一个和第二个 / 或 - 的位置 然后根据位置来补充0
3.第2个没问题 但是注意看我的第7点,hive的instr函数阉割了这个功能。
所以需要找其他函数 有两种
一种是通过 index获取 月份和天的具体下表 ,这个可以用subtring_index。
一种是通过正则直接提取。 我直接用这个regexp_extract了
select
y||'-'||if(length(m)=1,0||m,m)||'-'||if(length(d)=1,0||d,d)
from (
select
replace(a,'/','-') a ,regexp_extract(replace(a,'/','-'),'(\\d+)-(\\d+)-(\\d+)',1) y,
regexp_extract(replace(a,'/','-'),'(\\d+)-(\\d+)-(\\d+)',2) m,
regexp_extract(replace(a,'/','-'),'(\\d+)-(\\d+)-(\\d+)',3) d
from (
select '2020/3/23'as a union all
select '2019-12-1'as a union all
select '2019-12-6'as a union all
select '2018/7/6'as a
)t
)t1 ;
结果也ok,但是总感觉很lowb 有没有和oracle一样的to_date可以解决这个问题的呢? 我是暂时没看到,有没有小伙伴能够解决,可以留言,感激不尽。
---------------------------2023-02-24更新------------------------
最近发现了一个比较简单的算法。。。。
select to_date(replace(t.a ,'/','-')) from ( select '2020/3/23'as a union all select '2019-12-1'as a union all select '2019-12-6'as a union all select '2018/7/6'as a )t
oracle是真吉儿复杂。 简简单单的一个算第几周的函数搞这么复杂
oracle的iw算法,关于Oracle to_char()函数中的IW,WW 周别显示 ._liu威的博客-CSDN博客
hive是不行了。但是有个weekofyear()凑合用吧
oracle ww 初始不完整的一周算第一周 iw 是完整的第一周才算第一周
hive的还没尝试。。
这个函数其实和mysql的group_concat 差不多 ,都是列转行。
SELECT t.id ,to_char(WM_CONCAT(name )),WM_CONCAT(name )
FROM (
SELECT 1 AS id ,1 as name FROM dual UNION ALL
SELECT 1 AS id ,2 as name FROM dual UNION ALL
SELECT 1 AS id ,1 as name FROM dual UNION ALL
SELECT 2 AS id ,4 as name FROM dual UNION ALL
SELECT 2 AS id ,5 as name FROM dual
)t
GROUP BY t.id
等价于hive的
concat_ws(',',collect_list(column))
因为可以看到oracle没有去重,所以collect_list即可,不需要用collect_set
其实不想写这个。。之前遇到了几个简单的loop,我都通过shell来实现,后来发现之前的pkg写的是越来越过分。。。没办法只能加强的学习一波。
【Oracle】for in loop_Hi竹子的博客-CSDN博客
FOR 结果集 IN (
SELECT [匹配字段],[更新字段] FROM A表
) loop
UPDATE B表
SET B表.[需要更新字段]= 结果集.[更新字段];
WHERE
B表.[匹配字段]= 结果集.[匹配字段];
END loop ;---------------------------------------------------
SQL执行含义:
先执行IN里的SQL,得到结果集;
循环结果集,结果集可用于 loop里的SQL。
单看这么一段可能不太好理解。直接来实战 ---
不举例了。 其实简单的循环可以通过shell来实现。在shell里写while循环
有点实战不动了,我这边的例子里是个 for in loop , while () end loop 里面搞了一个递归,日了狗了,只能想着用spark代码去跑递归了。
这里我遇到了一个比较简单的循环,给大家展示下如何直接在hive里处理不经过shell和其他方法。
oracle pck
-- V_START_PERIOD := V_YEAR_ID || '01';
-- V_END_PERIOD := V_YEAR_ID || '12';--Part3、增长率计算:自动计算按增长率预估的数据
WHILE V_START_PERIOD <= V_END_PERIOD LOOPinsert into tableA
select v_start_period, column1 ,column2
from tableB
where v_start_period< tableB.period_id
V_START_PERIOD := V_START_PERIOD + 1;
END LOOP;
整个核心就是 从202301开始到202312 一次次比较tableB.period_id.符合就插入数据。
其实本质插入的数据基本差不多,只是有个v_start_period随着循环变化
---更新下 之前说错了 随着日期的变化 数量也在变化
where 202301< tableB.period_id 和 where 202302< tableB.period_id 的数据量肯定不一样
我遇到的有的where是固定的where 202301< tableB.period_id,
也有这种不固定的 where v_start_period< tableB.period_id
怎么用hive写呢?
固定的where 条件 where 202301< tableB.period_id,
核心思想 这个本质还是一份数据插入了多次。 一行变多行 就得拿出lateral view了。
with tmp as (
select *,
if(v_start_period+0
if(v_start_period+1 if(v_start_period+2 if(v_start_period+3 if(v_start_period+4 if(v_start_period+5 if(v_start_period+6 if(v_start_period+7 if(v_start_period+8 if(v_start_period+9 if(v_start_period+10 if(v_start_period+11 -- 这里就是比较每个月是否
from tableb
)
select
*,
tmp.period_id --这个id就是满足的条件的所有id
from tmp later view explode(concat_ws(',',month01,month02。。。。month12)) tmp as period_id
为什么上面用null不用’‘呢 因为concat_ws的时候如果是null直接忽略,如果是空就会是month01,,,,,,,,,,,,
当然这种改法有一定的局限性。比如不太适合循环太多的
oracle 大小写不敏感 YYYY-MM-DD HH24:MI:SS和yyyy-mm-dd hh24:mi:ss 都行
SELECT to_date('2023-01-02 15:55:03', 'yyyy-mm-dd hh24:mi:ss'),TO_DATE('2023-01-02 15:55:03', 'YYYY-MM-DD HH24:MI:SS')FROM dual
hive 标准格式 yyyy-MM-dd HH:mm:ss.SSS (当然yyyy和YYYYY看着一样) hh小写是12进制
select date_format('2023-03-17 16:23:37.330000000','yyyy-MM-dd HH:mm:ss.SSS')
,date_format('2023-03-17 16:23:37.330000000','YYYY-MM-DD hh:mm:SS.SSS')
因为hive有个类型自动转化问题,比如 id(int)='1' 也是可以查的,平常的时候确实很方便,但是有的地方会出大问题。
简单来说
hive '1'+1是有可能出问题的
oracle '1'+1 没有问题
select '999'>'1000' , 999 > 1000 -- true false
--前者为true 后者为false。 为啥呢? 因为这种比较大小对于字符串来说 是比较顺序,1000的顺序是小于999的顺序的,字符串的顺序是123456789
with t as (
select '1' a union all
select '11'a union all
select '2' a union all
select '3' a
)
select t.a from t order by awith t as (
select '1' a union all
select '11'a union all
select '2' a union all
select '3' a
)
select t.a from t order by cast(a as int )
怎么解决上述问题呢? 有时候我也不知道是字符string 还是int
可以将上面的改为
select '999'-'1000'>0,999-1000>0 即可。不过这个也会有点点小小的问题。
如下。
oracle 学习之 unpivot/pivot函数及hive实现该功能_cclovezbf的博客-CSDN博客
lpad函数从左边对字符串使用指定的字符进行填充基本语法:lpad( string, padded_length, [ pad_string ]
oracle
SELECT RPAD ('1234',10),LENGTH(RPAD ('1234',10)) FROM dual
-- 1234 10 --注意 1234后面又6个空格
SELECT RPAD ('1234',10,'a'),LENGTH(RPAD ('1234',10,'a')) FROM dual
1234aaaaaa 10
padded_length 就是你想要的位数有多长
pad_string 是默认空格,填充字符
hive
SELECT RPAD ('1234',10),LENGTH(RPAD ('1234',10)) --报错 必须有三个入参
正确写法
SELECT RPAD ('1234',10,' '),LENGTH(RPAD ('1234',10,' '))
1234 ,10
所以oracle 经常有 RPAD ('1234',10) 在hive里就需要多写一个空格 RPAD ('1234',10,' ')
继续更新 真是搞不动了 奉劝各位以后要是做oracle pck转hivesql的活 赶紧放弃吧,要是pck是你写的还行。不是你写的 真的是难搞。
很遗憾这两个b函数hive都没有。所以要想办法用啥替换呢?先学习下这两个函数,先佩服下oracle真是啥函数都有 一大堆。
Oracle分割字段的值并且返回多行数据(使用regexp_substr和regexp_count函数)_good_good_xiu的博客-CSDN博客
REGEXP_SUBSTR()
function REGEXP_SUBSTR(String, pattern, position, occurrence, modifier)
string:需要进行正则处理的字符串
pattern:进行匹配的正则表达式
position:起始位置,从字符串的第几个字符开始正则表达式匹配(默认为1) 注意:字符串最初的位置是1而不是0
occurrence:获取第几个分割出来的组(分割后最初的字符串会按分割的顺序排列成组)
modifier:模式(‘i’不区分大小写进行检索;‘c’区分大小写进行检索。默认为’c’)针对的是正则表达式里字符大小写的匹配
使用REGEXP_SUBSTR()进行逗号的匹配、字符串的分割和获取。
这个b玩意看起来和hive的split有点像
oracle
SELECT REGEXP_SUBSTR('1234,4567', '[^,]+', 1, 1) FROM dual; -- 1234
后面两个参数可以省略 默认都是1
SELECT REGEXP_SUBSTR('账号2543071133-腾讯云','\d+') FROM dual --2543071133
hive
select split('1234,4567',',')[0] -- 1234
其实还有一个函数就是regexp_extract
SELECT regexp_extract('账号2543071133-腾讯云','(\\d+)',1)
我有篇文章也提到这个函数
hive之正则函数研究学习regex/regex_replace/regex_extract_hive regex_cclovezbf的博客-CSDN博客首先学习这个之前要先知道一些正则的基本知识。随便百度一下将下一个字符标记为一个特殊字符、或一个原义字符、或一个 向后引用、或一个八进制转义符。例如,'n' 匹配字符 "n"。'\n' 匹配一个换行符。序列 '\\' 匹配 "\" 而 "\(" 则匹配 "("。匹配输入字符串的开始位置。如果设置了 RegExp 对象的 Multiline 属性,^ 也匹配 '\n' 或 '\r' 之后的位置。匹配输入字符串的结束位置。https://blog.csdn.net/cclovezbf/article/details/129422382
regexp_count()
Oracle的11g引入此函数
REGEXP_COUNT ( source_char, pattern [, position [, match_param]])
source_char:需要进行正则处理的字符串
pattern :进行匹配的正则表达式
position:起始位置,从字符串的第几个字符开始正则表达式匹配(默认为1) 注意:字符串最初的位置是1而不是0
match_param:‘i’ 用于不区分大小写的匹配,‘c’ 用于区分大小写的匹配,‘n’ 允许句点(.)作为通配符去匹配换行符。如果省略该参数,则句点将不匹配换行符,‘m’ 将源串视为多行。即Oracle 将^和$分别看作源串中任意位置任何行的开始和结束,而不是仅仅看作整个源串的开始或结束。如果省略该参数,则Oracle将源串看作一行。‘x’ 忽略空格字符。默认情况下,空格字符与自身相匹配。
REGEXP_COUNT 返回pattern 在source_char 串中出现的次数。如果未找到匹配,则函数返回0。position 变量告诉Oracle 在源串的什么位置开始搜索。在开始位置之后每出现一次模式,都会使计数结果增加1。
这个玩意感觉也和split有关系
oracle
SELECT REGEXP_COUNT('1234,5678',',') FROM dual -- 1 这个是正则出现的次数
SELECT REGEXP_COUNT('1234,5678',',') +1 FROM dual -- 2 这个是被切割的次数
这还好说 关键点来了。
这两个家伙加上connect by 组合使用 。
SELECT REGEXP_SUBSTR('1234,5678', '[^,]+', 1, ROWNUM)
FROM dual
CONNECT BY ROWNUM <= regexp_count('1234,5678', ',')+1;--1234
--5678注意这里是两列!!!!!!
这感觉突然一下子又简单起来了。 这个不就是一行变多行吗?
select tmp.a from (
select '1234,5678' as a
)t lateral view explode(split(a,',')) tmp as a
但是你乍一看oracle的这个语法不就是完了完了。这该怎么改? 其实就是hive的later view
oracle 存储过程里有这么一句话看似平平无奇
EXECUTE IMMEDIATE 'SELECT ' || LV_FORMULA || ' FROM DUAL'
INTO CALC_AMT;
但是这句话的实际含义是 把字符串LV_FORMULA='1+2+3+4' 转为计算结果
最后的 把结果1+2+3+4= 10赋值给calc_amt。。。也就是最后算出CALC_AMT=10
再简化下就是 某行 'a' '1+2+3+4' -> a 10 这么转化。
其实我已经有思路了。只是觉得不是特别好。(目前只适合加减法 不适合乘法) 仅供大家打开思路。 有好的想法可以留言让我学习。。。。
select a ,sum(s1)
from (
select a,b,tmp.s,
case
when tmp.s regexp '(\\+)' then regexp_extract(s, '(\\d+)') --这里如果是小数可以自行拓展
when tmp.s regexp '(\\-)' then negative(regexp_extract(s, '(\\d+)'))
else s end as s1
from (
select 'cc' a, '1+2+3+4' b
union all
select 'zbf' a, '1+2+3-4' b
) t lateral view explode(split(b, '(?!\\d)')) tmp as s
where length(s) > 0
)t
group by a
oracle
SELECT SYSDATE -1 FROM dual --获取昨天
SELECT to_date('2023-05-03','YYYY-MM-DD') -to_date('2023-05-02','YYYY-MM-DD') FROM dual --1 两天时间差
SELECT SYSDATE -to_date('2023-05-02','YYYY-MM-DD') FROM dual
--9.64637731481481481481481481481481481481 时间差 计算了时分秒
hive 其实也可以时间相减
select `current_timestamp`(), `current_timestamp`() -to_date('2023-05-10')
-- 2023-05-11 15:33:00.779000000,1 15:33:00.779000000 但是不推荐 因为看起来既直观又不直观
计算时间差一般采用的是datediff函数
select datediff(`current_timestamp`(),to_date('2023-05-10')) -- 1
计算前几天后几天采用date_add date_sub
select `current_date`(),date_add(`current_date`(),1),date_sub(`current_date`(),1)
2023-05-11,2023-05-12,2023-05-10
注意是没有计算时分秒功能的。