草庐IT

oracle转mysql语句转换

supuerlovepc 2024-02-12 原文

场景:Oracle数据库 -> TD数据库(腾讯),而TD数据库实质上是对mysql数据库的封装拓展。因此,需要对项目中oracle的sql进行适配性转化,即在sql查询结果不变的前提下,转换成符合mysql语法的sql语句。
tips:其实TD数据库8.0以上版本的语法大部分是可以完美兼容oracle语法的。但是我们的版本是5.7,因此许多函数和语法是不兼容oracle。如果你的td数据库版本在8以上,就不用往下看了。

一、oracle函数 -> mysql函数

1. decode -> case when


-- oracle: decode
select decode(1, 1, 'out1', 2, 'out2') from dual; -- out1
select decode(2, 1, 'out1', 2, 'out2') from dual; -- out2
select decode(3, 1, 'out1', 2, 'out2') from dual; -- null

--mysql: 1. case [col_name] when
select case 1 when 1 then 'out1' when 2 then 'out2' end; -- out1
select case 2 when 1 then 'out1' when 2 then 'out2' end; -- out2
select case 3 when 1 then 'out1' when 2 then 'out2' end; -- null

-- mysql: 2. case when [expr]
select case when 1 > 0  then 'out0' end; -- out0

12的区别在于,1case后只能跟列,而2case when后是可以跟表达式的,适合复杂情况

2. NVL(exp1,exp2) -> ifnull

-- oracle: nvl(param, val)
select nvl(null, 1) from dual; -- 1
select nvl('', 1) from dual;   -- 1
select nvl('0', 1) from dual;  -- 0

-- mysql: ifnull(param, val)
select ifnull(null,1);-- 1
select ifnull('',1);  -- ''
select ifnull('0',1); -- 0

nvl中null''都输出1,是因为oracle中,空值''即为null
select '' from dual;  -- null
而ifnull中''输出'',是与oracle不同的,因此在进行函数替换的时候,要注意,
如果条件值不为''时,才与nvl等价。如数字类型不会为''

如果param可能为''时,需要用case when等价替换,例如:
select case param when '' then 'empty' when null then 'nul' else 'val end';

3. NVL2(exp1,exp2,exp3) -> if(expr1,expr2,expr3)

-- oracle nvl2(exp1,exp2,exp3) 
select nvl2(1, '123', 'null') from dual;  -- 123
select nvl2('', '123', 'null') from dual;  -- null
select nvl2(null, '123', 'null') from dual;  -- null

--mysql if(param,notNullVal,nullVal) 
select if(1, 123, 'null');-- 123
select if('', 123, 'null');-- null
select if(null, 123, 'null');-- null

此时看起来是等同的,那就大错特错了。
接下来看数据库实际情况:

-- oacle: int_col_0 是int类型 值为0 
select nvl2(int_col_0, 123, null) from demo where id = '1'; -- 123
-- mysql: int_col_0 是int类型 值为0 
select if(int_col_0, 123, null) from demo where id = '1'; -- null
这时候两个函数的结果就不一样了。
原因如下:if(param,notNullVal,nullVal)函数中,如果param是varchar类型,当param
值为''时,会输出后值;如果param时int类型,当param值为0时,会输出后值;当param是
decimal类型,当param为0.0时,会输出后者。
得出结论:当param的值为其数据类型的默认值时,if()函数会走后者。
因此,在param数据类型为varchar时,与nvl2函数是等价的。

4. TO_NUMBER(exp) -> CAST(value AS datatype)

-- 这个没什么讲的,可以直接用,在mysql中cast函数用于将数据类型强转为其它数据类型
类似于java。
select CAST('123' AS decimal(5,1) ); -- 123.0

5. to_char
a. to_char(date, dateFormatType) -> date_format(date, dateFormatType)

-- oracle: to_char(date, dateFormatType)
select to_char(sysdate, 'yyyymmdd') from dual; -- 20230222

-- mysql: date_format(date, dateFormatType)
select date_format(now(), '%Y%m%d'); -- 20230222

b. to_char(str) -> CAST(value AS char)


-- oracle to_char
select to_char(sysdate) from dual; -- [varchar2] 22-2月 -23

--mysql: cast(value AS char)
select cast(now() as char ); -- [varcahr]2023-02-22 09:10:02

6. TRUNC() -> DATE()

-- oracle trunc()
SELECT TRUNC(sysdate) from DUAL; -- [DATE]2023-02-21 00:00:00
SELECT TRUNC(to_date('20210101','yyyymmdd')) from DUAL; -- [DATE]2021-01-01 00:00:00

-- mysql date()
SELECT DATE(NOW()); -- [DATE]2023-02-21
SELECT DATE('20210201'); -- [DATE]2021-02-01

7. to_date -> str_to_date

-- oracle: to_date
select to_date('20210101', 'yyyymmdd') from dual; -- 2021-01-01 00:00:00

-- mysql : str_to_date
select str_to_date('20210101', '%Y%m%d'); -- 2021-01-01

8. 常用时间格式转换

oracle  ->  mysql
YYYYmmdd -> %Y%m%d
YYYY-mm-dd -> %Y-%m-%d 
yyyy-mm-dd hh24:mi:ss -> %Y-%m-%d %H:%i:%s

9. 时间计算方式
a. 日期+/-1天

-- oracle 
select to_date('20210101', 'yyyymmdd') - 1 from dual; -- 2020-12-31 00:00:00

-- mysql
select str_to_date('20210101', '%Y%m%d') - 1 from dual; -- 20210100
从结果可以看出是错误的,只是单纯-1,并没有校验时间是否正确。

-- mysql adddate(date, [num])
select adddate(str_to_date('20210101', '%Y%m%d'), -1); -- 2020-12-31

-- mysql date_sub/date_add(date, interval [num] [day/month/year...])
select date_sub(str_to_date('20210101', '%Y%m%d'), interval 1 day ); -- 2020-12-31
select date_add(str_to_date('20210101', '%Y%m%d'), interval 1 day ); -- 2021-01-02

其中interval后可跟如下时间单位:
FRAC_SECOND
SECOND
MINUTE
HOUR
DAY
WEEK
MONTH
QUARTER
YEAR

b. 日期+/-1月/年

-- oracle  add_months(date, num)
select add_months(to_date('20210101','yyyymmdd'), 1) from dual; -- 2021-02-01 00:00:00

-- mysql date_add(date,, interval [num] month)
select date_add(str_to_date('20210101', '%Y%m%d'), interval 1 month ); --2021-02-01

-- mysql 
SELECT TIMESTAMPADD(SQL_TSI_MONTH, 1, str_to_date('20210101', '%Y%m%d')); -- 2021-02-01

tips:
mybatis中使用date_add时,一旦里卖弄interval关键字,总是会sql编译失败,
不知道是否和版本还是什么有关。

使用TIMESTAMPADD函数不会报错。

interval-type间隔类型参数如下:
SQL_TSI_FRAC_SECOND
SQL_TSI_SECOND
SQL_TSI_MINUTE
SQL_TSI_HOUR
SQL_TSI_DAY
SQL_TSI_WEEK
SQL_TSI_MONTH
SQL_TSI_QUARTER
SQL_TSI_YEAR

10. SUBSTR() -> SUBSTRING()

-- 截取字符串:
注意:
-- oracle
select SUBSTR('123456', 1, 3)、SUBSTR('123456', 0, 3) from dual; -- 输出都是123;

-- mysql
select SUBSTRING('123456', 1, 3); -- 输出123  
select  SUBSTRING('123456', 0, 3); --输出空值''

11. 拼接符号|| -> concat(str, str1)

-- mysql concat(str1,str2,...)
select concat('1','2','3','4','A'); -- 1234A

12. length() -> char_length()

-- oracle length()
select char_length(''); -- null
select LENGTH('123'), '123' from dual; -- 3
select LENGTH(0.00), 0.00 from dual; -- 1
select LENGTH(0), 0 from dual; -- 1
select LENGTH(0.000001), 123456 from dual; -- 7
select LENGTH(sysdate), sysdate from dual; -- 9 2023-02-22 01:23:39

-- mysql char_length
select char_length(''); -- 0
select char_length(123); -- 3
select char_length(0); -- 1
select char_length(0.0); -- 3
select char_length(0.000001); -- 8
select char_length(now()), now(); -- 19  2023-02-22 09:18:34

tips:
可以看出,oracle的length()对于字符串的长度计算结果是符合我们预期的,但是数字类型和并不会把
小数点的长度加上。且当为0.0000时,会自动转会为0再做长度计算,得出结果为1.时间格式
时掺长度我也不确定是怎么得出的。
mysql的char_length()结果就比较符合我们的期望。会把所有参数都转化为char类型后,再进行
长度计算。

注意:当字符串计算长度时,如果字符串为空'',oracle-length()的结果为null,mysql-
char_length的结果为0

二、oracle语法 -> mysql语法

有些语法也不是通用的,下面是经常见到的几种。
1. 临时表with as
解决方案1:换成子查询放到表调用处
解决方案2:在java中拆分,减少sql复杂度。

2. 左右连接(+)写法
解决方案:根据原sql where后的表连接条件,有(+)的换成left join,没有的换成inner join

3. 递归 connect by prior
解决方案:这块内容比较多,另开了一篇:[‘@实现mysql递归查询’]
4. merge语法
解决方案:sql拆成查询、更新、插入三个语句。在java中组合调用

有关oracle转mysql语句转换的更多相关文章

  1. ruby-on-rails - 在 Rails 中将文件大小字符串转换为等效千字节 - 2

    我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,

  2. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  3. ruby - 将数组的内容转换为 int - 2

    我需要读入一个包含数字列表的文件。此代码读取文件并将其放入二维数组中。现在我需要获取数组中所有数字的平均值,但我需要将数组的内容更改为int。有什么想法可以将to_i方法放在哪里吗?ClassTerraindefinitializefile_name@input=IO.readlines(file_name)#readinfile@size=@input[0].to_i@land=[@size]x=1whilex 最佳答案 只需将数组映射为整数:@land边注如果你想得到一条线的平均值,你可以这样做:values=@input[x]

  4. ruby - 将散列转换为嵌套散列 - 2

    这道题是thisquestion的逆题.给定一个散列,每个键都有一个数组,例如{[:a,:b,:c]=>1,[:a,:b,:d]=>2,[:a,:e]=>3,[:f]=>4,}将其转换为嵌套哈希的最佳方法是什么{:a=>{:b=>{:c=>1,:d=>2},:e=>3,},:f=>4,} 最佳答案 这是一个迭代的解决方案,递归的解决方案留给读者作为练习:defconvert(h={})ret={}h.eachdo|k,v|node=retk[0..-2].each{|x|node[x]||={};node=node[x]}node[

  5. ruby-on-rails - Ruby url 到 html 链接转换 - 2

    我正在使用Rails构建一个简单的聊天应用程序。当用户输入url时,我希望将其输出为html链接(即“url”)。我想知道在Ruby中是否有任何库或众所周知的方法可以做到这一点。如果没有,我有一些不错的正则表达式示例代码可以使用... 最佳答案 查看auto_linkRails提供的辅助方法。这会将所有URL和电子邮件地址变成可点击的链接(htmlanchor标记)。这是文档中的代码示例。auto_link("Gotohttp://www.rubyonrails.organdsayhellotodavid@loudthinking.

  6. ruby - 如何在 Ruby 中向现有方法定义添加语句 - 2

    我注意到类定义,如果我打开classMyClass,并在不覆盖的情况下添加一些东西我仍然得到了之前定义的原始方法。添加的新语句扩充了现有语句。但是对于方法定义,我仍然想要与类定义相同的行为,但是当我打开defmy_method时似乎,def中的现有语句和end被覆盖了,我需要重写一遍。那么有什么方法可以使方法定义的行为与定义相同,类似于super,但不一定是子类? 最佳答案 我想您正在寻找alias_method:classAalias_method:old_func,:funcdeffuncold_func#similartoca

  7. ruby-on-rails - 使用 ruby​​ 将多个实例变量转换为散列的更好方法? - 2

    我收到格式为的回复#我需要将其转换为哈希值(针对活跃商家)。目前我正在遍历变量并执行此操作:response.instance_variables.eachdo|r|my_hash.merge!(r.to_s.delete("@").intern=>response.instance_eval(r.to_s.delete("@")))end这有效,它将生成{:first="charlie",:last=>"kelly"},但它似乎有点hacky和不稳定。有更好的方法吗?编辑:我刚刚意识到我可以使用instance_variable_get作为该等式的第二部分,但这仍然是主要问题。

  8. 使用canal同步MySQL数据到ES - 2

    文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co

  9. python ffmpeg 使用 pyav 转换 一组图像 到 视频 - 2

    2022/8/4更新支持加入水印水印必须包含透明图像,并且水印图像大小要等于原图像的大小pythonconvert_image_to_video.py-f30-mwatermark.pngim_dirout.mkv2022/6/21更新让命令行参数更加易用新的命令行使用方法pythonconvert_image_to_video.py-f30im_dirout.mkvFFMPEG命令行转换一组JPG图像到视频时,是将这组图像视为MJPG流。我需要转换一组PNG图像到视频,FFMPEG就不认了。pyav内置了ffmpeg库,不需要系统带有ffmpeg工具因此我使用ffmpeg的python包装p

  10. ruby-on-rails - 将字符串转换为 ruby​​-on-rails 中的函数 - 2

    我需要一个通过输入字符串进行计算的方法,像这样function="(a/b)*100"a=25b=50function.something>>50有什么方法吗? 最佳答案 您可以使用instance_eval:function="(a/b)*100"a=25.0b=50instance_evalfunction#=>50.0请注意,使用eval本质上是不安全的,尤其是当您使用外部输入时,因为它可能包含注入(inject)的恶意代码。另请注意,a设置为25.0而不是25,因为如果它是整数a/b将导致0(整数)。

随机推荐