
异构数据库的到P的语迁移(譬如从Oracle迁移到PostgreSQL)工作主要包括三个方面,
数据库对象的据库迁移,包括库、法迁模式、移手表、到P的语索引、据库视图、法迁触发器、移手存储过程等等;数据内容的到P的语迁移,主要指的据库是数据表中的数据;数据应用的迁移,主要指的法迁是应用中SQL语句的迁移。目前对于数据库对象以及数据内容的移手迁移有很多成熟的工具,而对于应用迁移的到P的语工具却很少能够见到。原因是据库因为DML语句比DDL复杂的多,不同的高防服务器法迁数据库语法差异也比较大。目前市场上的迁移工具大多使用正则表达式来解析SQL语句,而DML语句的复杂性导致此类工具的解析成功率较低,难以作为一个成熟地商业产品进行推广。
PawSQL团队开发的DML语法转换工具Ora2pg,通过PawSQL强大的SQLParser,能够解析几乎所有的Oracle语法,并将其转换为对应的PostgreSQL语法,支持数据库应用的平滑迁移。
本手册介绍了Oracle和PostgreSQL的语法区别,以及转换映射关系,可以作为迁移人员的SQL迁移参考手册。
本手册描述了PawSQL Ora2pg内部的实现逻辑,PawSQL Ora2pg能够帮助SQL迁移人员自动识别不兼容的语法,并完成语法转换。
Oracle获取一个常量需要通过一个dual,PostgreSQL不需要
编号
Oracle
PostgreSQL
1
select 2 from dual
select 2
对于查询返回的每行数据,rownum虚拟列会返回一个数字,第一行的ROWNUM为1,站群服务器第二行为2,以此类推。
rownum在select列表中时重写为row_number() over ()rownum在where子句中时重写为limit... offset...编号
Oracle
PostgreSQL
1
selectrownumfrom customer;
selectrow_number() over () as rownum from customer
2
select tableoid from customer whererownum < 10 and rownum >= 2;
select tableoid from customerlimit 9 OFFSET 2
3
select c_name from customer whererownum < 10and c_phone = 111;
select customer.c_name from customer where customer.c_phone = 111limit 9
4
select * from customer whererownum between 1 and 10;
select tableoid from customerlimit 10 OFFSET 0
虚拟列rowidOracle中的rowid虚拟列返回特定行的具体地址,在PostgreSQL中重写为tableoid || # || ctid。
编号
Oracle
PostgreSQL
1
selectrowid, c.* from customer c;
select tableoid||#||ctid, c.* from customer as c
Oracle中的nvl(col, value)用来设置默认值,col为空就设置为value;在PostgreSQL中重写为coalesce。
编号
Oracle
PostgreSQL
1
selectnvl(c_phone, 1)from customer;
selectcoalesce(customer.c_phone, 1) from customer
nvl2(col, v1, v2)nvl2对col的null值进行处理,如果col为null,则返回v1, 否则返回v2;postgre中没有类似的函数,可以重写为case... when...
编号
Oracle
PostgreSQL
1
selectnvl2(c_phone, 1, 2)from customer;
selectcase when c_phone is null then 1 else 2 end from customer
decode(arg1, arg2, arg3, arg4)Oracle中的decode(arg1, arg2, arg3, arg4)函数, 表示当 arg1 等于 arg2 时,取 arg3,否则取 arg4。
postgre中没有类似的函数,可以重写为case... when...
编号
Oracle
PostgreSQL
1
selectdecode(c_phone,110, 1 , 2)from customer;
selectcase when c_phone = 110 then 1 else 2 end from customer
2
selectdecode(c_phone,null, 1 , 2)from customer;
selectcase when c_phone is null then 1 else 2 end from customer
substr(str, int, int)Oracle中的substr用来取一个字符串的子串,PostgreSQL有同名的函数实现类似功能。不同的是Oracle中,第二、第三个参数可以为负数,代表从后面进行计数,PostgreSQL不允许其为负数,企商汇需对其进行转换。Oracle中是以0开始计数,PostgreSQL以1开始计数(需确认)。
编号
Oracle
PostgreSQL
1
select substr(c_phone, 1 ,-2) from customer;
select substr(c_phone, 1,length(c_phone) - 2) from customer
2
select substr(c_phone,-3, 1 ) from customer;
select substr(c_phone,length(c_phone) - 3, 1) from customer
instr(str1, str2)Oracle中的instr用来取一个字符串的子串位置,当其只有两个参数时,表示子串的第一次出现的位置,和PostgreSQL中对应的函数为strpos。当其有多个参数时,无对应函数。
编号
Oracle
PostgreSQL
1
selectinstr(123, 23)
select strpos(123, 23)
replace(srcstr, oldsub[, newsub ])在Oracle中,replace()函数用于替换字符串, replace(srcstr, oldsub[, newsub ] ),和PostgreSQL中的replace函数用法基本一致。只是需要注意在Oracle中无第三个参数时,代表删除此字符,在PostgreSQL可将第三个参数设置为。
编号
Oracle
PostgreSQL
1
selectreplace(123,1);
selectreplace(123,1,);
stragg(str,[str])Oracle里的stragg函数实现在分组内对列值的拼接,它和listagg类似,但是不可以指定拼接的顺序。在PostgreSQL中,可以使用string_agg函数来替换。其第二个参数可选,默认值为,在PostgreSQL需补充第二个参数。
编号
Oracle
PostgreSQL
1
selectlistagg(c_name,,) as name from customer group by c_phone
selectstring_agg(c_name,,) as name from customer group by c_phone
2
selectlistagg(c_name) as name from customer group by c_phone
selectlistagg(c_name,) as name from customer group by c_phone
listagg(str, [str])Oracle里的listagg函数实现对列值的拼接,它可以在分组内以指定顺序对非分组列进行拼接。在PostgreSQL中,可以使用string_agg函数来实现,需注意语法方面也有区别. 另外,其第二个参数可选,默认值为,在PostgreSQL需补充第二个参数。
当没有group by子句时,可以使用over(partiton by... order by...)进行替换。当指定group by子句时,它的重写算法比较复杂。如果需要保持拼接的顺序,需要通过子查询来实现(见编号2)如果不需要保持拼接顺序,可以把它转化为简单的聚集函数(编号3)编号
Oracle
PostgreSQL
1
selectlistagg(c_name,,)withingroup(orderbyc_name)over(partitionbyc_phone) as name from customer;
sselectstring_agg(customer.c_name, ,)over (partition by customer.c_phone order by c_custkey)as name from customer
2
select listagg(c_name,,) within group(order by c_name) as name from customer group by c_phone;
select max(paw_dt.name) as name from (selectstring_agg(customer.c_name, ,) over (partition by customer.c_phone order by c_name)as name, customer.c_phone from customer) as paw_dt group by c_phone
3
selectlistagg(c_name,,)within group(order by c_name)as name from customer group by c_phone
selectstring_agg(c_name,,) as name from customer group by c_phone
Oracle中的sysdate()/sysdate返回系统当前时间(日期+时分秒),在PostgreSQL中对应now()或是current_timestamp(日期+时分秒+毫秒)。
Oracle中的systimestamp返回系统当前时间戳(日期+时分秒+毫秒),在PostgreSQL中对应now()或是current_timestamp。
编号
Oracle
PostgreSQL
1
selectsysdate
selectcurrent_timestamp
2
selectsysdate()
selectnow()
3
selectsystimestamp
selectcurrent_timestamp
to_date(str, fmt)Oracle中的to_date返回的是时间类型,而在PostgreSQL中to_date是日期类型,所以Oracle中的to_date在PostgreSQL中应该对应to_timestamp。
编号
Oracle
PostgreSQL
1
selectto_date( endTime ,yyyy-mm-dd hh24:mi:ss)from t
selectto_timestamp( endTime ,yyyy-mm-dd hh24:mi:ss)from t
trunc(arg1, [arg2])在Oracle中trunc函数有两种用法。
第一种是对数字进行截取, trunc(num,[int]); 是去掉数字num小数位以后的部分,并且不进行四舍五入。这种用法和在PostgreSQL的trunc用法一致,不需要转换。trunc函数的第二种用法是对日期进行提取,trunc(date,[fmt])。这种用法在PostgreSQL对应的函数是date_trunc(fmt, date),需注意在PostgreSQL中fmt是第一个参数,且不可省略。编号
Oracle
PostgreSQL
1
selecttrunc( 111.23,2)
selecttrunc( 111.23,2)
2
selecttrunc(sysdate,year)
selectdate_trunc(year, current_timestamp)
3
selecttrunc(sysdate)
selectdate_trunc(dd, current_timestamp)
add_months(date, int)Oracle中的add_months 函数主要是对日期函数进行操作,对日期按月增加。在PostgreSQL没有对应的函数,需将其转化为基于日期和interval的运算。
编号
Oracle
PostgreSQL
1
selectadd_months(sysdate, 2)
select current_timestamp +2 * interval 1 month
last_day(date)Oracle中的last_day返回指定日期所在月份的最后一天; 在PostgreSQL没有对应的函数,需将其转化为基于日期和interval的运算。
编号
Oracle
PostgreSQL
1
selectadd_months(sysdate, 2)
selectcast(date_trunc(MONTH, current_timestamp)+ interval 1 MONTH - 1 DAY as date)
Oracle允许HAVING在GROUP BY子句之前或之后。在PostgreSQL中,HAVING子句必须出现在GROUP BY子句后面。
编号
Oracle
PostgreSQL
1
select c_name from customerhaving count(*) > 2group by c_name
select c_name from customer group by c_name having count(*) > 2
括号中的表名Oracle中单表引用允许使用括号括起来,PostgreSQL不允许。
编号
Oracle
PostgreSQL
1
SELECT * FROM (CUSTOMER);
SELECT * FROM CUSTOMER;
UNIQUE关键字Oracle中允许使用UNIQUE进行去重,在PostgreSQL中迁移为DISTINCT关键字。
编号
Oracle
PostgreSQL
1
selectuniquec_phone from customer
select distinct customer.c_phone from customer
MINUS关键字Oracle中可以使用minus关键字来取两个结果集的差,在PostgreSQL中需迁移为except。
编号
Oracle
PostgreSQL
1
select c_custkey from customerminusselect o_custkey from orders
select c_custkey from customerexceptselect o_custkey from orders
FROM关键字Oracle的delete语句的FROM关键字可以省略,迁移至PostgreSQL需补充上。
编号
Oracle
PostgreSQL
1
delete customer where 1=0;
deletefromcustomer where 1 = 0
NOLOGGING关键字Oracle在执行INSERT语句时,可以通过指定NOLOGGING关键字来减少日志记录,提升操作性能。PostgreSQL不支持此关键字。
编号
Oracle
PostgreSQL
1
insert into customernologgingselect * from customer_bk;
insert into customer select * from customer_bk;
AS关键字INSERT INTO 后面不需要添加as关键字,insert into ... as select... 修改为insert into... select...
编号
Oracle
PostgreSQL
1
insert into tasselect c1 from t1
insert into t select c1 from t1
FROM子查询的别名Oracle中在不引起歧义的情况下子查询可以不带别名,而在PostgreSQL中,所有的FROM子查询都必须带有别名。
编号
Oracle
PostgreSQL
1
select * from (select * from CUSTOMER)
select * from (select * from CUSTOMER)as foo
UPDATE语句里的字段名在PostgreSQL中,Update的时候,更新列不允许添加表名前缀。
编号
Oracle
PostgreSQL
1
update customer c setc.c_name= xxx where c_custkey = 1;
update customer setc_name= xxx where c_custkey = 1
左(右)外连接在Oracle中,外连接可以通过在条件上添加(+)来定义, 连接符(+)跟在哪个条件后面就是哪张表被左连。在PostgreSQL中,需将其重写为标准的外连接语法。
编号
Oracle
PostgreSQL
1
select * from customer, orders wherec_custkey = o_custkey(+)
select * from customerleft outer joinorders onc_custkey = o_custkey
2
select * from customer, orders wherec_custkey(+)= o_custkey andc_name(+)= o.o_clerk and o_custkey>100
select * fromcustomerright outer joinorders on (c_custkey = o_custkey and c_name = o_clerk) where o_custkey > 100
CONNECT BY子句Oracle中,CONNECT BY 用于存在上下级等层级关系的数据表进行递归查询。语法格式: START WITH condition1 CONNECT BY [ NOCYCLE ] condition2。在PostgreSQL通过Recursive Common Table Expression来实现此功能,主要是把START WITH... CONNECT BY Prior拆成两个部分,查询表一致,但条件不一致,用UNION ALL合并。
编号
Oracle
PostgreSQL
1
select id from city_branchstart withid=1connect by priorid=parent_id;
withRECURSIVE MIG_CTEas (select id, 1 as level from city_branch where id = 1 union allselect id, level + 1 from city_branch, MIG_CTE where MIG_CTE.id = parent_id)select * from MIG_CTE
2
select t.branch_level, t.id from city_branch c where (c.branch_level = 1 or t.branch_level = 2) and (t.sign = 1 or t.sign = 4 or t.sign = 8) and t.status = 1start withc.id = 1connect byc.id =priorc.parent_id order by c.branch_level desc
withRECURSIVE MIG_CTEas (select t.branch_level, t.id, 1 as level from city_branch as cwhere ((((branch_level = 1 or t.branch_level = 2)and ((t.sign = 1 or t.sign = 4) or t.sign = 8)) and t.status = 1) and c.id = 1)union allselect t.branch_level, t.id, level + 1 from city_branch as c, MIG_CTE where ((((branch_level = 1 or t.branch_level = 2) and ((t.sign = 1 or t.sign = 4) or t.sign = 8)) and t.status = 1) and c.id = MIG_CTE.parent_id))select * from MIG_CTE order by MIG_CTE.branch_level desc
操作符的强类型限制Oracle中不同类型进行基于操作符的运算,会自动转化类型,譬如select 1 + 1 from dual。PostgreSQL是强类型,不同类型的运算会提示类型不匹配,执行select 1 + 1会报错,需要进行显式的类型转换。
涉及的操作符类型包括:
操作符
操作符名称
+
加法
-
减法
/
除法
%
取余
*乘法
||
字符串拼接
数值运算(+,-,*,/,%)编号
Oracle
PostgreSQL
1
select 1 +1
select 1 +1
2
select 1 +charColfrom tbl
select 1 +cast(charCol as numeric)from tbl
3
select 1- 1
select 1- 1
4
select 1 *charColfrom tbl
select 1 *cast(charCol as numeric)from tbl
5
select 1 /charColfrom tbl
select 1 /cast(charCol as numeric)from tbl
6
selectcharCol% 2 from tbl
selectcast(charCol as numeric)% 2 from tbl
日期计算(+,-)编号
Oracle
PostgreSQL
1
select sysdate- 1
select current_timestamp- interval 1 DAY
2
select 1 + sysdate()
selectinterval 1 DAY+ now()
3
select systimestamp+1
select current_timestamp+ interval 1 DAY
4
select systimestamp- 1
select current_timestamp- interval 1 DAY
字符串拼接(||)编号
Oracle
PostgreSQL
1
select1||1
select1||1
2
select 1||c_custkey
select 1||cast(c_custkey as text)
Oracle中在函数调用时,参数类型进行会自动转化类型,譬如 select substr(123.12,0,2)是合法的,且返回123。PostgreSQL是强类型, 执行select substr(123.12,0,2)会报错,需要进行显式的类型转换。
substr(arg1, arg2, arg3)编号
Oracle
PostgreSQL
1
select substr(1234.1, 0, 4)
select substr(1234.1, 1, 4+1)
2
select substr(1234.1, 0,2)
select substr(1234.1, 0,2)
sum(arg)编号
Oracle
PostgreSQL
1
selectsum(2)
selectsum(2)
avg(arg)编号
Oracle
PostgreSQL
1
selectavg(2)
selectavg(2)
round(arg)编号
Oracle
PostgreSQL
1
selectround(2)
selectround(2)
Oracle中在进行条件判断时,左右表达式的类型进行会自动转化,譬如 where c_phone = 110是合法的。PostgreSQL是强类型, 执行where c_phone = 110会报错,需要进行显式的类型转换。
比较运算(=、>、<、>=、<=、<>)转换原则,优先转换常量类型;当两个都为数据列时,优先转换左边的。
编号
Oracle
PostgreSQL
1
select * from customer wherec_phone = 110
select * from customer wherec_phone = 110
2
select * from customer where1 = c_custkey
select * from customer where1 = c_custkey
3
select * from customer wherec_phone = c_custkey
select * from customer wherecast(c_phone as int) = c_custkey
BETWEEN转换原则,转换 var0 between var1 and var2 中的 var1, var2。
编号
Oracle
PostgreSQL
1
select * from customer where c_custkeybetween 100 and 200;
select * from customer where c_custkeybetween 100 and 200
IN LIST转换原则,转换List中的变量。
编号
Oracle
PostgreSQL
1
select * from customer wherec_phone in (110,120);
select * from customer wherec_phone in (110, 120)
Oracle中有部分函数存在默认参数,而在PostgreSQL其参数是必填项。
to_char(unknown)编号
Oracle
PostgreSQL
1
selectto_char(c_custkey)from customer
selectcast(c_custkey as text) from customer
to_number(str)编号
Oracle
PostgreSQL
1
selectto_number(100)
select100
2
selectto_number(c_phone)from customer;
selectcast(c_phone as numeric) from customer