AnalyticDB for PostgreSQL(简称:ADB for PG)对Oracle语法有着较好的兼容,本文介绍如何将Oracle应用迁移到AnalyticDB for PostgreSQL。

1 PL/SQL

PL/SQL(Procedural Language/SQL)是一种过程化的SQL语言,是Oracle对SQL语句的拓展,使得SQL的使用可以具有一般编程语言的特点,因此,可以用来实现复杂的业务逻辑。PL/SQL对应了ADB for PG中的PL/PGSQL

1.1Package

ADB for PG的plpgsql不支持package,需要把package 转换成 schema,并package里面的所有procedure和 function转换成ADB for PG的function。
例如:

create or replace package pkg is
…
end;

可以转换成:

create schema pkg;
  1. Package定义的变量

     procedure/function的局部变量保持不变,全局变量在ADB for PG中可以使用临时表进行保存。详见1.4.5节。
  2. Package初始化块

    如果可以删掉,就删掉,删不掉的话,可以使用function封装,在需要的时候主动调用该function。
  3. Package 内定义的procedure/function

    Package 内定义的procedure和function 转成adb for pg的function,并把function 定义到package对应的schema内。
    例如,有一个Package名为pkg中有如下函数:
    FUNCTION test_func (args int) RETURN int is
    var number := 10;
    BEGIN
    … …
    END;
    转换成如下ADB for PG的function:
    CREATE OR REPLACE FUNCTION pkg. test_func(args int) RETURNS int AS
    $$… …  $$LANGUAGE plpgsql;

1.2 Procedure/function

对于oracle的procedure和function,不论是package的还是全局的,都转换成adb for pg 的function。
例如:

CREATE OR REPLACE FUNCTION test_func (v_name varchar2, v_version varchar2)
RETURN varchar2 ISret varchar(32);
BEGINIF v_version IS NULL THENret := v_name;
ELSEret := v_name || '/' || v_version;END IF;RETURN ret;
END;

转化成:

CREATE OR REPLACE FUNCTION test_func (v_name varchar, v_version varchar)
RETURNS varchar AS
$$DECLAREret varchar(32);
BEGINIF v_version IS NULL THENret := v_name;
ELSEret := v_name || '/' || v_version;END IF;RETURN ret;
END;$$LANGUAGE plpgsql;

Procedure/function转换的关键点:

  1. RETURN 关键字转成RETURNS
  2. 函数体使用$\$ ... $\$封装起来
  3. 函数语言声明
  4. Subprocedure需要转换成ADB for PG的function

1.3 PL statement

1.3.1 For语句

带有REVERSE的整数FOR循环的工作方式不同:PL/SQL中是从第二个数向第一个数倒数,而PL/pgSQL是从第一个数向第二个数倒数,因此在移植时需要交换循环边界。
示例:

FOR i IN REVERSE 1..3 LOOPDBMS_OUTPUT.PUT_LINE (TO_CHAR(i));
END LOOP;

转换成:

FOR i IN REVERSE 3..1 LOOPRAISE ‘%’ ,i;
END LOOP;

1.3.2 PRAGMA语句

ADB for PG 无PRAGMA语句,删除。

1.3.3 事务处理

ADB for PG 的function 内部无法使用事务控制语句,如begin,commit,rollback等。
修改方法:

  1. 删除函数体内的事务控制语句,把事务控制放在函数体外;
  2. 把函数按照commit/rollback 拆分成多个。

1.3.4 EXECUTE语句

ADB for PG支持类似oracle的动态sql语句,不同之处如下:

  1. 不支持using 语法,解决方法是把参数拼接到sql串中;
  2. 数据库标识符使用quote_ident包裹,数值使用quote_literal包裹。

示例:

EXECUTE 'UPDATE employees_temp SET commission_pct = :x' USING a_null;

转换成:

EXECUTE 'UPDATE employees_temp SET commission_pct = ' || quote_literal(a_null);

1.3.5 Pipe row

Pipe row函数,使用adb for pg的table function来替换。
示例:

TYPE pair IS RECORD(a int, b int);
TYPE numset_t IS TABLE OF pair;FUNCTION f1(x int) RETURN numset_t PIPELINED IS
DECLAREv_p pair;
BEGINFOR i IN 1..x LOOPv_p.a := i;v_p.b := i+10;PIPE ROW(v_p);END LOOP;RETURN;
END;select * from f1(10);

转换成:

create type pair as (a int, b int);create or replace function f1(x int) returns setof pair as
$$declare
rec pair;
beginfor i in 1..x looprec := row(i, i+10);return next rec;end loop;return ;
end$$language 'plpgsql';select * from f1(10);

说明:

  1. 自定义类型pair转换成adb for pg的复合类型pair
  2. Table of类型不需要定义,使用adb for pg的setof 替换
  3. Pipe row 语句转换成下面两个语句:

      rec := row(i);return next rec;
  4. 上面的oracle function还可以转换成如下:

    create or replace function f1(x int) returns setof record as
    $$declare
    rec record;
    beginfor i in 1..x looprec := row(i, i+10);return next rec;end loop;return ;
    end$$
    language 'plpgsql';

与第一种改法的不同支持是,不需要提前定义数据类型numset_t.正因为这一点所以在查询的时候需要指定返回的类型,如下:select * from f1(10) as (a int, b int);

1.3.6 异常处理

  1. 使用raise抛出异常
  2. Catch异常后,不能rollback事务,只能在udf外做rollback
  3. ADB for PG支持的error,可以参考: https://www.postgresql.org/docs/8.3/errcodes-appendix.html

1.3.7 function中同时有Return和OUT参数

在adb pg中,不允许fucntion同时有return和out参数,因此,可以把需要返回的参数改写成out类型参数。

示例:

CREATE OR REPLACE FUNCTION test_func(id int, name varchar(10), out_id out int) returns varchar(10)
AS $body$
BEGINout_id := id + 1;return name;
end
$body$
LANGUAGE PLPGSQL;

改写成:

CREATE OR REPLACE FUNCTION test_func(id int, name varchar(10), out_id out int, out_name out varchar(10))
AS $body$
BEGINout_id := id + 1;out_name := name;
end
$body$
LANGUAGE PLPGSQL;

然后select * from test_func(1,’1’) into rec;从rec中取对应字段的返回值即可。

1.4 PL数据类型

1.4.1 Record

使用ADB for PG的复合数据类型替换
示例:

TYPE rec IS RECORD (a int, b int);

改写成:

CREATE TYPE rec AS (a int, b int);

1.4.2 Nest table

  1. Nest table 作为pl 变量,可以使用ADB for PG的array类型替换。
    示例:
DECLARETYPE Roster IS TABLE OF VARCHAR2(15);names Roster := Roster('D Caruso', 'J Hamil', 'D Piro', 'R Singh');
BEGINFOR i IN names.FIRST .. names.LASTLOOPIF names(i) = 'J Hamil' THENDBMS_OUTPUT.PUT_LINE(names(i));END IF;END LOOP;
END;

改写成:

create or replace function f1() returns void as
$$declarenames varchar(15)[] := '{"D Caruso", "J Hamil", "D Piro", "R Singh"}';len int := array_length(names, 1);
beginfor i in 1..len loopif names[i] = 'J Hamil' thenraise notice '%', names[i];end if;end loop;return ;
end$$language 'plpgsql';select f();
  1. 作为function返回值,则可以使用table function替换,参考1.3.5节。

1.4.3 Associative Array

无替换类型。

1.4.4 Variable-Size Arrays

与nest table 一样,使用array类型替换。

1.4.5 Global variables

目前ADB for PG不支持global variables,一种方法是把一个package中的所有global variables存入一张临时表(temporary table)中, 然后定义修改、获取global variables的函数。

示例:

create temporary table global_variables (id int,g_count int,g_set_id varchar(50),g_err_code varchar(100)
);insert into global_variables values(0, 1, null,null);CREATE OR REPLACE FUNCTION get_variable() returns setof global_variables AS$$DECLARErec global_variables%rowtype;
BEGINexecute 'select * from global_variables' into rec;return next rec;
END;$$LANGUAGE plpgsql;CREATE OR REPLACE FUNCTION set_variable(in param varchar(50), in value anyelement) returns void AS$$BEGINexecute 'update global_variables set ' ||  quote_ident(param) || ' = ' || quote_literal(value);
END;$$LANGUAGE plpgsql;

其中,临时表global_variables中,字段id为这个表的分布列,因为ADB for PG中不允许对于分布列的修改,需要多加一个这样的字段。
tmp_rec record;
修改一个全局变量时,使用:select * from set_variable(‘g_error_code’, ‘error’::varchar) into tmp_rec;
获取一个全局变量时,使用:select * from get_variable() into tmp_rec; error_code := tmp_rec.g_error_code;

1.5 SQL

1.5.1 Connect by

Oracle 层次查询,adb for pg没有等价替换的sql语句。转换思路是使用循环按层次遍历。
示例:

create table employee(emp_id numeric(18),lead_id numeric(18),emp_name varchar(200),salary numeric(10,2),dept_no varchar(8)
);
insert into employee values('1',0,'king','1000000.00','001');
insert into employee values('2',1,'jack','50500.00','002');
insert into employee values('3',1,'arise','60000.00','003');
insert into employee values('4',2,'scott','30000.00','002');
insert into employee values('5',2,'tiger','25000.00','002');
insert into employee values('6',3,'wudde','23000.00','003');
insert into employee values('7',3,'joker','21000.00','003');
insert into employee values('3',7,'joker','21000.00','003');
select emp_id,lead_id,emp_name,prior emp_name as lead_name,salaryfrom employeestart with  lead_id=0connect by prior emp_id =  lead_id

转换成:

create or replace function f1(tablename text, lead_id int, nocycle boolean) returns setof employee as
$$declareidx int := 0;res_tbl varchar(265) := 'result_table';prev_tbl varchar(265) := 'tmp_prev';curr_tbl varchar(256) := 'tmp_curr';current_result_sql varchar(4000);tbl_count int;rec record;
beginexecute 'truncate ' || prev_tbl;execute 'truncate ' || curr_tbl;execute 'truncate ' || res_tbl;loop-- 查询当前层次结果,并插入到tmp_curr表current_result_sql := 'insert into ' || curr_tbl || ' select t1.* from ' || tablename || ' t1';if idx > 0 thencurrent_result_sql := current_result_sql || ', ' || prev_tbl || ' t2 where t1.lead_id = t2.emp_id';elsecurrent_result_sql := current_result_sql || ' where t1.lead_id = ' || lead_id;end if;execute current_result_sql;-- 如果有环,删除已经遍历过的数据if nocycle is false thenexecute 'delete from ' || curr_tbl || ' where (lead_id, emp_id) in (select lead_id, emp_id from ' || res_tbl || ') ';end if;-- 如果没有数据,则退出execute 'select count(*) from ' || curr_tbl into tbl_count;exit when tbl_count = 0;-- 把tmp_curr数据保存到result表execute 'insert into ' || res_tbl || ' select * from ' || curr_tbl;execute 'truncate ' || prev_tbl;execute 'insert into ' || prev_tbl || ' select * from ' || curr_tbl;execute 'truncate ' || curr_tbl;idx := idx + 1;end loop;-- 返回结果current_result_sql := 'select * from ' || res_tbl;for rec in execute current_result_sql loopreturn next rec;end loop;return;
end$$language plpgsql;

1.5.2 Rownum

  1. 限定查询结果集大小,可以使用limit替换
    示例:
select * from t where rownum < 10;

转换成:

select * from t limit 10;
  1. 使用row_number() over()生成rownum
    示例:

select rownum, * from t;
转换成:

select row_number() over() as rownum, * from t;

1.5.3 Dual表

  1. 去掉dual
    示例:
select sysdate from dual;

转换成:

select current_timestamp;
  1. 创建一个叫dual的表。

1.5.4 Select中的udf

ADB for PG支持在select中调用udf,但是udf中不能有sql语句,否则会收到如下的错误信息:
ERROR: function cannot execute on segment because it accesses relation "public.t2" (functions.c:155) (seg1 slice1 127.0.0.1:25433 pid=52153) (cdbdisp.c:1326)
DETAIL:
SQL statement "select b from t2 where a = $1 "

转换方法是把select中的udf转换成sql表达式或者子查询等
示例:

create or replace FUNCTION f1(arg int) RETURN int ISv int;
BEGINselect b into v from t2 where a = arg;return v;
END;select a, f1(b) from t1;

转换成:

select t1.a, t2.b from t1, t2 where t1.b = t2.a;

1.5.5 (+)多表外链接

ADB for PG 不支持(+)这样的语法形式,需要转换成标准的outer join语法。
示例:

oracle
select * from a,b where a.id=b.id(+)

转换成:

select * from a left join b on a.id=b.id

如果在(+)中有三表的join,需要先用wte做两表的join,再用+号那个表跟wte表做outer join。
示例:

Select * from test1 t1, test2 t2, test3 t3 where t1.col1(+) between NVL(t2.col1, t3.col1) and NVL(t3.col1, t2.col1);

转换成:

with cte as (select t2.col1 as low, t2.col2, t3.col1 as high, t3.col2 as c2 from t2, t3)
select * from t1 right outer join cte on t1.col1 between coalesce(cte.low, cte.high) and coalesce(cte.high,cte.low);

1.5.6 Merge into

对于merge into语法的转换,在ADB for PG中先使用update进行更新,然后使用GET DIAGNOSTICS rowcount := ROW_COUNT;语句获取update更新的行数,如果update更新的行数为0,那么再使用insert语句进行插入。

MERGE INTO test1 t1USING (SELECT t2.col1 col1, t3.col2 col2,FROM test2 t2, test3 t3) SON S.col1 = 1 and S.col2 = 2
WHEN MATCHED THENUPDATESET test1.col1 = S.col1+1,test1.col2 = S.col2+2
WHEN NOT MATCHED THENINSERT (col1, col2)VALUES(S.col1+1, S.col2+2);

转换成:

Update test1 t1 SET t1.col1 = test2.col1+1, test3.col2 = S.col2+2 where test2.col1 = 1 and test2.col2 = 2;
GET DIAGNOSTICS rowcount := ROW_COUNT;
if rowcount = 0 theninsert into test1 values(test2.col1+1, test3.col2+2);

2 系统函数转换对照表

oracle ADB for PG
sysdate current timestamp
trunc trunc/ date trunc
dbms_output.put_line raise 语句
decode 转成case when/直接使用decode
NVL coalesce

3 数据类型转换对照表

oracle ADB for PG
sysdate current timestamp
trunc trunc/ date trunc
dbms_output.put_line raise 语句
decode 转成case when/直接使用decode
NVL coalesce
oracle ADB for PG
VARCHAR2 varchar or text
DATE timestamp
LONG text
LONG RAW bytea
CLOB text
NCLOB text
BLOB bytea
RAW bytea
ROWID oid
FLOAT double precision
DEC decimal
DECIMAL decimal
DOUBLE PRECISION double precision
INT int
INTERGE integer
REAL real
SMALLINT smallint
NUMBER numeric
BINARY_FLOAT double precision
BINARY_DOUBLE double precision
TIMESTAMP timestamp
XMLTYPE xml
BINARY_INTEGER integer
PLS_INTEGER integer
TIMESTAMP WITH TIME ZONE timestamp with time zone
TIMESTAMP WITH LOCAL TIME ZONE timestamp with time zone

原文链接
本文为云栖社区原创内容,未经允许不得转载。

Oracle应用迁移到AnalyticDB for PostgreSQL指导相关推荐

  1. Teradata应用迁移到AnalyticDB for PostgreSQL指导

    AnalyticDB for PostgreSQL(简称:ADB for PG)对Teradata语法有着很好的兼容,将Teradata应用迁移到ADB for PG,只需进行有限的修改.本文介绍将T ...

  2. 如何将数据仓库从 AWS Redshift 迁移到阿里云 AnalyticDB for PostgreSQL

    阿里云AnalyticDB for PostgreSQL(以下简称 ADB PG,即原HybridDB for PostgreSQL)为基于PostgreSQL内核的MPP架构的实时数据仓库服务,可以 ...

  3. Oracle数据库迁移postgreSQL

    Oracle数据库迁移PostgreSQL 本文将记录一下Oracle数据库迁移至PostgreSQL数据库的整个过程,主要使用Navicat Premium 16以及pgAdmin 4来实现.参考: ...

  4. 聚水潭是如何基于AnalyticDB for PostgreSQL 构筑海量实时数仓平台的

    聚水潭数据仓库业务介绍 上海聚水潭网络科技有限公司成立于2014年.聚水潭创建之初,以电商SaaS ERP切入市场,凭借出色的产品和服务,快速获得市场领先地位.随着客户需求的不断变化,如今聚水潭已经发 ...

  5. Tableau BI工具对接 AnalyticDB for PostgreSQL数据源

    AnalyticDB for PostgreSQL(原HybridDB for PostgreSQL)作为高性能分析型数据库,可以支持用户对其业务数据进行实时分析,能够让企业敏锐感知市场动态,做出必要 ...

  6. 使用ADAM将Oracle数据库迁移至阿里云数据库PolarDB

    背景 什么是PolarDB? 以下内容引用自阿里云官方网站: 阿里云自研的云原生关系型数据库PolarDB有三个独立的引擎,分别100%兼容MySQL.100%兼容PostgreSQL.高度兼容Ora ...

  7. Oracle数据库迁移-基础

    Oracle数据库迁移-基础 作为一个开发人员,数据库知识肯定是需要我们掌握的.但是目前公司的数据库都是有专门的DBA维护的,一般都是给我们一个环境地址,然后我们书写CRUD SQL.而且公司产品要求 ...

  8. oracle / parallle /,Oracle海量数据迁移之使用shell启用多个动态并行

    在Oracle数据迁移中,可能有成百上千个表,有些表很大,有些表又很 在Oracle数据迁移中,可能有成百上千个表,有些表很大,有些表又很小. 如果启用了多个并行的进程,可能会有资源分配上的问题. 比 ...

  9. Oracle数据库迁移问题(备份、还原、数据导入导出、PLSQL)IMP - 0009 错误

    最近在做Oracle数据迁移,从一个服务器迁移到另外一台服务器上去,遇到各种问题,比如 导入DMP文件的时候,遇到 IMP - 0009 错误,折腾了半天,发现原来是我 连接到服务器导出DMP文件时, ...

最新文章

  1. webpack学习之路
  2. 第三课--EFM32GG11系列--串口接收不定长度数据的几种方式
  3. OO第三次博客作业——规格
  4. mysql5.7空间运算,深度解析MySQL5.7之临时表空间
  5. django-模板语言-判断语句
  6. (动态规划 最长有序子序列)Monkey and Banana --HDU --1069
  7. 选择什么技术,才能不被淘汰?180 所高校在增设这个专业!
  8. 用GO语言开发editplus编辑器插件(附源码)
  9. python日期,时间函数
  10. QT分析之网络编程(七)
  11. 停掉暴风影音stormliv.exe进程
  12. IC面试数字电路基础
  13. 关于出现Merge remote-tracking branch ‘origin/develop‘ into develop这种commit记录的原因
  14. mui赋值_mui input用法
  15. 《茅屋为秋风所破歌》古诗鉴赏
  16. mongo数据库取并集
  17. JS实现动画特效2(缓动函数封装、导航栏筋斗云效果)
  18. Ubuntu18.04重启后进入图形化界面,鼠标和键盘失灵。(并不是界面卡死哟,因为屏幕上的时间还在走,你看我这个小机灵。)
  19. MPSOC DP协议介绍
  20. 2021最新某团_token参数分析、可获取商家列表、商家详情数据

热门文章

  1. php实现多条件查找分页,Yii2.0框架实现带分页的多条件搜索功能示例
  2. sql 查询表结构_SQL查询语句的完整结构解析
  3. python 类方法 函数_Python OOP类中的几种函数或方法总结
  4. 运行catia_CATIA清除错误目录及防串链接
  5. 金华职业技术学院计算机应用技术分数线,金华职业技术学院录取分数线2021是多少分(附历年录取分数线)...
  6. java编译_解析 Java 即时编译器原理。
  7. 组建实验室仅3年,团队人均26岁,这位85后女博导成果登上Nature!
  8. 在读博士练成健身达人,女大学生在宿舍练出八块腹肌……
  9. 90后女科学家,四年完成清华大学硕博连读,解决多个世界级难题
  10. 我在网上抢火车票:多加了100元的加速包,却依然买不到票