Oracle应用迁移到AnalyticDB for PostgreSQL指导
AnalyticDB for PostgreSQL(簡稱:ADB for PG)對Oracle語法有著較好的兼容,本文介紹如何將Oracle應(yīng)用遷移到AnalyticDB for PostgreSQL。
1 PL/SQL
PL/SQL(Procedural Language/SQL)是一種過程化的SQL語言,是Oracle對SQL語句的拓展,使得SQL的使用可以具有一般編程語言的特點,因此,可以用來實現(xiàn)復(fù)雜的業(yè)務(wù)邏輯。PL/SQL對應(yīng)了ADB for PG中的PL/PGSQL
1.1Package
ADB for PG的plpgsql不支持package,需要把package 轉(zhuǎn)換成 schema,并package里面的所有procedure和 function轉(zhuǎn)換成ADB for PG的function。
例如:
可以轉(zhuǎn)換成:
create schema pkg;Package定義的變量
procedure/function的局部變量保持不變,全局變量在ADB for PG中可以使用臨時表進行保存。詳見1.4.5節(jié)。Package初始化塊
如果可以刪掉,就刪掉,刪不掉的話,可以使用function封裝,在需要的時候主動調(diào)用該function。Package 內(nèi)定義的procedure/function
Package 內(nèi)定義的procedure和function 轉(zhuǎn)成adb for pg的function,并把function 定義到package對應(yīng)的schema內(nèi)。 例如,有一個Package名為pkg中有如下函數(shù): FUNCTION test_func (args int) RETURN int is var number := 10; BEGIN … … END; 轉(zhuǎn)換成如下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的還是全局的,都轉(zhuǎn)換成adb for pg 的function。
例如:
轉(zhuǎn)化成:
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轉(zhuǎn)換的關(guān)鍵點:
1.3 PL statement
1.3.1 For語句
帶有REVERSE的整數(shù)FOR循環(huán)的工作方式不同:PL/SQL中是從第二個數(shù)向第一個數(shù)倒數(shù),而PL/pgSQL是從第一個數(shù)向第二個數(shù)倒數(shù),因此在移植時需要交換循環(huán)邊界。
示例:
轉(zhuǎn)換成:
FOR i IN REVERSE 3..1 LOOPRAISE ‘%’ ,i; END LOOP;1.3.2 PRAGMA語句
ADB for PG 無PRAGMA語句,刪除。
1.3.3 事務(wù)處理
ADB for PG 的function 內(nèi)部無法使用事務(wù)控制語句,如begin,commit,rollback等。
修改方法:
1.3.4 EXECUTE語句
ADB for PG支持類似oracle的動態(tài)sql語句,不同之處如下:
示例:
EXECUTE 'UPDATE employees_temp SET commission_pct = :x' USING a_null;轉(zhuǎn)換成:
EXECUTE 'UPDATE employees_temp SET commission_pct = ' || quote_literal(a_null);1.3.5 Pipe row
Pipe row函數(shù),使用adb for pg的table function來替換。
示例:
轉(zhuǎn)換成:
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);說明:
Pipe row 語句轉(zhuǎn)換成下面兩個語句:
rec := row(i);return next rec;上面的oracle function還可以轉(zhuǎn)換成如下:
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';與第一種改法的不同支持是,不需要提前定義數(shù)據(jù)類型numset_t.正因為這一點所以在查詢的時候需要指定返回的類型,如下:select * from f1(10) as (a int, b int);
1.3.6 異常處理
1.3.7 function中同時有Return和OUT參數(shù)
在adb pg中,不允許fucntion同時有return和out參數(shù),因此,可以把需要返回的參數(shù)改寫成out類型參數(shù)。
示例:
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中取對應(yīng)字段的返回值即可。
1.4 PL數(shù)據(jù)類型
1.4.1 Record
使用ADB for PG的復(fù)合數(shù)據(jù)類型替換
示例:
改寫成:
CREATE TYPE rec AS (a int, b int);1.4.2 Nest table
示例:
改寫成:
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.4.3 Associative Array
無替換類型。
1.4.4 Variable-Size Arrays
與nest table 一樣,使用array類型替換。
1.4.5 Global variables
目前ADB for PG不支持global variables,一種方法是把一個package中的所有g(shù)lobal variables存入一張臨時表(temporary table)中, 然后定義修改、獲取global variables的函數(shù)。
示例:
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語句。轉(zhuǎn)換思路是使用循環(huán)按層次遍歷。
示例:
轉(zhuǎn)換成:
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-- 查詢當(dāng)前層次結(jié)果,并插入到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;-- 如果有環(huán),刪除已經(jīng)遍歷過的數(shù)據(jù)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;-- 如果沒有數(shù)據(jù),則退出execute 'select count(*) from ' || curr_tbl into tbl_count;exit when tbl_count = 0;-- 把tmp_curr數(shù)據(jù)保存到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;-- 返回結(jié)果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
示例:
轉(zhuǎn)換成:
select * from t limit 10;示例:
select rownum, * from t;
轉(zhuǎn)換成:
1.5.3 Dual表
示例:
轉(zhuǎn)換成:
select current_timestamp;1.5.4 Select中的udf
ADB for PG支持在select中調(diào)用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 "
轉(zhuǎn)換方法是把select中的udf轉(zhuǎn)換成sql表達式或者子查詢等
示例:
轉(zhuǎn)換成:
select t1.a, t2.b from t1, t2 where t1.b = t2.a;1.5.5 (+)多表外鏈接
ADB for PG 不支持(+)這樣的語法形式,需要轉(zhuǎn)換成標(biāo)準(zhǔn)的outer join語法。
示例:
轉(zhuǎn)換成:
select * from a left join b on a.id=b.id如果在(+)中有三表的join,需要先用wte做兩表的join,再用+號那個表跟wte表做outer join。
示例:
轉(zhuǎn)換成:
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語法的轉(zhuǎn)換,在ADB for PG中先使用update進行更新,然后使用GET DIAGNOSTICS rowcount := ROW_COUNT;語句獲取update更新的行數(shù),如果update更新的行數(shù)為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);轉(zhuǎn)換成:
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 系統(tǒng)函數(shù)轉(zhuǎn)換對照表
| sysdate | current timestamp |
| trunc | trunc/ date trunc |
| dbms_output.put_line | raise 語句 |
| decode | 轉(zhuǎn)成case when/直接使用decode |
| NVL | coalesce |
3 數(shù)據(jù)類型轉(zhuǎn)換對照表
| sysdate | current timestamp |
| trunc | trunc/ date trunc |
| dbms_output.put_line | raise 語句 |
| decode | 轉(zhuǎn)成case when/直接使用decode |
| NVL | coalesce |
| 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 |
原文鏈接
本文為云棲社區(qū)原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載。
總結(jié)
以上是生活随笔為你收集整理的Oracle应用迁移到AnalyticDB for PostgreSQL指导的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: OSS在线迁移服务剖析
- 下一篇: 拔掉数据库的电源会怎样?阿里云数据库新型