Oracle PL/SQL应用迁移 AnalyticDB for PostgreSQL指导

本文涉及的产品
阿里云百炼推荐规格 ADB PostgreSQL,4核16GB 100GB 1个月
RDS PostgreSQL Serverless,0.5-4RCU 50GB 3个月
推荐场景:
对影评进行热评分析
简介: AnalyticDB for PostgreSQL(简称:ADB for PG)对Oracle语法有着较好的兼容,本文介绍如何将Oracle应用迁移到AnalyticDB for PostgreSQL。

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

1 开源工具ora2pg使用

在开始转换Oracle pl之前可以使用开源工具ora2pg(https://github.com/darold/ora2pg)进行最初始的转换。具体的使用,用户可以阅读其用户文档,在此不做赘述。用户可以使用ora2pg,将oracle的表DDL,view,package等转换成PG兼容的语法。但是,由于脚本转换后的PG语法版本比ADB for PG使用的PG内核版本高,而且ora2pg依赖规则进行转换,难免有所疏漏和错误,因此用户还需要人工去对转换后的sql脚本做纠正。具体推荐的做法是:可以将转换后的sql脚本在ADB for PG上试运行,观察是否有语法报错或者结果不符预期,然后在依据本文接下来几个章节的内容,进行人工纠正。

2 Orafunc插件使用

ADB for PG提供了Orafunc插件的使用,该插件提供了一些兼容Oracle的函数。对于这些函数,用户无需任何修改转换就可以直接在ADB for PG中使用。
在使用Orafunc插件前,只需执行create extension orafunc;命令即可。

postgres=> create extension orafunc;
CREATE EXTENSION
AI 代码解读

Orafunc插件提供的兼容函数如下表所示:

函数 作用 示例
nvl(anyelement, anyelement) 如果第一个参数为null,那么会返回第二个参数;否则返回第一个参数。注意:两个参数必须是相同类型。 postgres=# select nvl(null,1);
nvl
-----
  1
(1 row)
postgres=# select nvl(0,1);
nvl
-----
  0
(1 row)
postgres=# select nvl(0,null);
nvl
-----
  0
(1 row)
add_months(day date, value int)
RETURNS date
在第一个月份参数上加上第二个参数月数,返回一个date postgres=# select add_months(current_date, 2);
add_months
------------
2019-08-31
(1 row)
last_day(value date) 返回某年某个月份的最后一个天,返回类型为date postgres=# select last_day('2018-06-01');
 last_day
------------
2018-06-30
(1 row)
next_day(value date, weekday text) 参数一:开始的日期
参数二:包含星期几的英文字符串,如Friday
返回开始日期后的第二个星期几的日期,如第二个Friday
postgres=# select next_day(current_date, 'FRIDAY');
 next_day
------------
2019-07-05
(1 row)
next_day(value date, weekday integer) 参数一:开始的日期
参数二:星期几的数字,取值为1到7,1为星期日,2为星期1,以此类推
返回日期加天数之后的日期
postgres=# select next_day('2019-06-22', 1);
 next_day
------------
2019-06-23
(1 row)
postgres=# select next_day('2019-06-22', 2);
 next_day
------------
2019-06-24
(1 row)
months_between(date1 date, date2 date) 返回date1和date2之间的月数
如果 date1晚于date2,结果为正
如果date1早于date2,结果为负
postgres=# select months_between('2019-01-01', '2018-11-01');
months_between
----------------
             2
(1 row)
postgres=# select months_between('2018-11-01', '2019-01-01');
months_between
----------------
            -2
(1 row)
trunc(value timestamp with time zone, fmt text) 参数一:要被截断的timestamp
参数二:应用于截断的度量单位,如年,月,日,周,时,分,秒等,Y:会截断成日期年份的第一天,Q返回季度的第一天。
postgres=# SELECT TRUNC(current_date,'Q');
  trunc
------------
2019-04-01
(1 row)
postgres=# SELECT TRUNC(current_date,'Y');
  trunc
------------
2019-01-01
(1 row)
trunc(value timestamp with time zone) 截断timestamp,默认截断时分秒 postgres=# SELECT TRUNC('2019-12-11'::timestamp);
        trunc
------------------------
2019-12-11 00:00:00+08
(1 row)
trunc(value date) 截断日期 postgres=# SELECT TRUNC('2019-12-11'::timestamp,'Y');
        trunc
------------------------
2019-01-01 00:00:00+08
round(value timestamp with time zone, fmt text) 将timestamp圆整到最近的unit_of_measure(日,周等) postgres=# SELECT round('2018-10-06 13:11:11'::timestamp, 'YEAR');
        round
------------------------
2019-01-01 00:00:00+08
(1 row)
round(value timestamp with time zone) 默认圆整到天 postgres=# SELECT round('2018-10-06 13:11:11'::timestamp);
        round
------------------------
2018-10-07 00:00:00+08
(1 row)
round(value date, fmt text) 参数为date postgres=# SELECT round(TO_DATE('27-OCT-00','DD-MON-YY'), 'YEAR');
  round
------------
2001-01-01
(1 row)
postgres=# SELECT round(TO_DATE('27-FEB-00','DD-MON-YY'), 'YEAR');
  round
------------
2000-01-01
(1 row)
round(value date) 参数为date ostgres=# SELECT round(TO_DATE('27-FEB-00','DD-MON-YY'));
  round
------------
2000-02-27
(1 row)
instr(str text, patt text, start int, nth int) 在一个string中搜索一个substring,如果找到返回substring在string中位置,没找到,则返回0
start表示搜索的起始位置
nth表示搜索第几次出现的位置
postgres=# SELECT instr('Greenplum', 'e',1,2);
instr
-------
    4
(1 row)
postgres=# SELECT instr('Greenplum', 'e',1,1);
instr
-------
    3
(1 row)
instr(str text, patt text, start int) 没给nth,默认是第一次出现的位置 postgres=# SELECT instr('Greenplum', 'e',1);
instr
-------
    3
(1 row)
instr(str text, patt text) 没给start,默认从头开始搜索 postgres=# SELECT instr('Greenplum', 'e');
instr
-------
    3
(1 row)
reverse(str text, start int, _end int) str为输入的字符串
start,end分别为对字符串从start到end这一段进行逆序
postgres=> select reverse('adb4pg', 5,6);
reverse
---------
gp
reverse(str text, start int) 从start开始到字符串结束进行逆序 ostgres=> select reverse('adb4pg', 4);
reverse
---------
gp4
reverse(str text) 逆序整个字符串 postgres=> select reverse('adb4pg');
reverse
---------
gp4bda
(1 行记录)
concat(text, text) 将两个字符串拼接在一起 postgres=> select concat('adb','4pg');
concat
--------
adb4pg
(1 行记录)
concat(text, anyarray)/concat(anyarray, text)/concat(anyarray, anyarray) 可以拼接任意类型的数据 postgres=> select concat('adb4pg', 6666);
  concat
------------
adb4pg6666
(1 行记录)
postgres=> select concat(6666, 6666);
 concat
----------
66666666
(1 行记录)
postgres=> select concat(current_date, 6666);
    concat
----------------
2019-06-306666
(1 行记录)
nanvl(float4, float4)
/nanvl(float4, float4)/nanvl(numeric, numeric)
如果第一个参数是数值类型的,则返回第一个参数。如果不是,则返回第二参数 postgres=> select nanvl('NaN', 1.1);
nanvl
-------
  1.1
(1 行记录)
postgres=> select nanvl('1.2', 1.1);
nanvl
-------
  1.2
(1 行记录)
bitand(bigint, bigint) 将两个整形的二进制做and操作,并返回and之后的结果,只输出一行 postgres=# select bitand(1,3);
bitand
--------
     1
(1 row)
postgres=# select bitand(2,6);
bitand
--------
     2
(1 row)
postgres=# select bitand(4,6);
bitand
--------
     4
(1 row)
listagg1_transfn(text, text) 最后输出一个数组(多行),第二个参数会放在每行结果的最后,相当于分割符 postgres=> SELECT listagg1_transfn(t, '.') FROM (VALUES('abc'), ('def')) as l(t);
listagg1_transfn
------------------
abc.
def.
listagg2_transfn(text, text, text) 最后输出一个数组(多行),第二个参数会放在每行结果的最后,相当于分割符,第三个参数会和第一个参数做聚集
listagg(text) 将文本值聚集成一个串 postgres=> SELECT listagg(t) FROM (VALUES('abc'), ('def')) as l(t);
listagg
---------
abcdef
(1 行记录)
listagg(text, text) 将文本值聚集成一个串,第二个参数执行了分割符 postgres=> SELECT listagg(t, '.') FROM (VALUES('abc'), ('def')) as l(t);
listagg
---------
abc.def
(1 行记录)
nvl2(anyelement, anyelement, anyelement) 如果第一个参数不为null,那么返回第二个参数,如果为null,则返回第三个参数 postgres=> select nvl2(null, 1, 2);
nvl2
------
   2
(1 行记录)
postgres=> select nvl2(0, 1, 2);
nvl2
------
   1
(1 行记录)
lnnvl(bool) 如果参数为null或者false,则返回true,如果为true,则返回false postgres=> select lnnvl(null);
lnnvl
-------
t
(1 行记录)
postgres=> select lnnvl(false);
lnnvl
-------
t
(1 行记录)
postgres=> select lnnvl(true);
lnnvl
-------
f
(1 行记录)
dump("any") 返回一个文本值,该文本中包含第一个参数的数据类型代码、以字节计的长度和内部表示。 postgres=> select dump('adb4pg');
                dump
---------------------------------------
Typ=705 Len=7: 97,100,98,52,112,103,0
(1 行记录)
dump("any", integer) 第二个参数表示返回文本值的内部表示是使用10进制还是16进制,目前只支持10和16 postgres=> select dump('adb4pg', 10);
                dump
---------------------------------------
Typ=705 Len=7: 97,100,98,52,112,103,0
(1 行记录)
postgres=> select dump('adb4pg', 16);
               dump
------------------------------------
Typ=705 Len=7: 61,64,62,34,70,67,0
(1 行记录)
postgres=> select dump('adb4pg', 2);
ERROR:  unknown format (others.c:430)
nlssort(text, text) 指定排序规则的排序数据函数 create table t1 (name text);
INSERT INTO t1 VALUES('Anne'), ('anne'), ('Bob'), ('bob');
postgres=> select from t1 order by nlssort(name, 'en_US.UTF-8');
name
------
anne
Anne
bob
Bob
(4 行记录)
postgres=> select
from t1 order by nlssort(name, 'C');
name
------
Anne
Bob
anne
bob
(4 行记录)
substr(str text, start int) 获取参数一中字符串的子串。
参数二表示获取子串的起始位置 >= start
postgres=> select substr('adb4pg', 1);
substr
--------
adb4pg
(1 行记录)
postgres=> select substr('adb4pg', 4);
substr
--------
4pg
(1 行记录)
substr(str text, start int, len int) 参数三会指定子串的结束位置
>= start and <= end
postgres=> select substr('adb4pg', 5,6);
substr
--------
pg
(1 行记录)
pg_catalog.substrb(varchar2, integer, integer) varchar2类型的获取子串函数,参数二为start pos,参数三为end pos postgres=> select substr('adb4pg'::varchar2, 5,6) ;
substr
--------
pg
(1 行记录)
pg_catalog.substrb(varchar2, integer) varchar2类型的获取子串函数,参数二为start pos,从start pos一直取到字符串结束 postgres=> select substr('adb4pg'::varchar2, 4) ;
substr
--------
4pg
(1 行记录)
pg_catalog.lengthb(varchar2) 获取varchar2类型字符串占的字节数,如果输入为null返回null,输入为空字符,则返回0 ostgres=> select lengthb('adb4pg'::varchar2) ;
lengthb
---------
      6
(1 行记录)
postgres=> select lengthb('分析型'::varchar2) ;
lengthb
---------
      9
(1 行记录)

Orafunc插件除了提供上述兼容函数,还对Oracle的Varchar2数据类型提供了兼容。

另外,对于下面的四个Oracle函数,在ADB for PG中,无需安装orafunc插件就可以提供兼容支持:

函数 作用 示例
sinh(float) 双曲正弦值 postgres=# select sinh(0.1);
      sinh
-------------------
0.100166750019844
(1 row)
tanh(float) 双曲正切值 postgres=# select tanh(3);
      tanh
------------------
0.99505475368673
(1 row)
cosh(float) 双曲余弦值 postgres=# select cosh(0.2);
      cosh
------------------
1.02006675561908
(1 row)
decode(expression, value, return [,value, return]...
       [, default])
在表达是中寻找一个搜索值,被找到返回指定的值。如果没有搜索到,返回default值。 create table t1(id int, name varchar(20));
postgres=# insert into t1 values(1,'alibaba');
postgres=# insert into t1 values(2,'adb4pg');
postgres=# select decode(id, 1, 'alibaba', 2, 'adb4pg', 'not found') from t1;
 case
---------
alibaba
adb4pg
(2 rows)
postgres=# select decode(id, 3, 'alibaba', 4, 'adb4pg', 'not found') from t1;
  case
-----------
not found
not found
(2 rows)

3 数据类型转换对照表

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

4 系统函数转换对照表

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

5 PL/SQL

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

5.1Package

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

create or replace package pkg isend;
AI 代码解读

可以转换成:

create schema pkg;
AI 代码解读
  1. Package定义的变量

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

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

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

5.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 IS
    ret varchar(32);
BEGIN
    IF v_version IS NULL THEN
        ret := v_name;
ELSE
    ret := v_name || '/' || v_version;
    END IF;
    RETURN ret;
END;
AI 代码解读

转化成:

CREATE OR REPLACE FUNCTION test_func (v_name varchar, v_version varchar)
RETURNS varchar AS 
$$

DECLARE
    ret varchar(32);
BEGIN
    IF v_version IS NULL THEN
        ret := v_name;
ELSE
    ret := v_name || '/' || v_version;
    END IF;
    RETURN ret;
END;

$$
 LANGUAGE plpgsql;
AI 代码解读

Procedure/function转换的关键点:

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

5.3 PL statement

5.3.1 For语句

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

FOR i IN REVERSE 1..3 LOOP
    DBMS_OUTPUT.PUT_LINE (TO_CHAR(i));
END LOOP;
AI 代码解读

转换成:

FOR i IN REVERSE 3..1 LOOP
    RAISE ‘%’ ,i;
END LOOP;
AI 代码解读

5.3.2 PRAGMA语句

ADB for PG 无PRAGMA语句,删除。

5.3.3 事务处理

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

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

5.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;
AI 代码解读

转换成:

EXECUTE 'UPDATE employees_temp SET commission_pct = ' || quote_literal(a_null);
AI 代码解读

5.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
DECLARE
    v_p pair;
BEGIN
    FOR i IN 1..x LOOP
      v_p.a := i;
      v_p.b := i+10;
      PIPE ROW(v_p);
    END LOOP;
    RETURN;
END;

select * from f1(10);
AI 代码解读

转换成:

create type pair as (a int, b int);

create or replace function f1(x int) returns setof pair as 
$$

declare
rec pair;
begin
    for i in 1..x loop
        rec := row(i, i+10);
        return next rec;
    end loop;
    return ;
end

$$
 language 'plpgsql';

select * from f1(10);
AI 代码解读

说明:

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

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

    create or replace function f1(x int) returns setof record as 
    $$
    
    declare
    rec record;
    begin
        for i in 1..x loop
            rec := row(i, i+10);
            return next rec;
        end loop;
        return ;
    end
    
    $$
    language 'plpgsql';
    AI 代码解读

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

5.3.6 异常处理

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

5.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$
BEGIN
      out_id := id + 1;
      return name;
end
$body$
LANGUAGE PLPGSQL;
AI 代码解读

改写成:

CREATE OR REPLACE FUNCTION test_func(id int, name varchar(10), out_id out int, out_name out varchar(10))
AS $body$
BEGIN
      out_id := id + 1;
      out_name := name;
end
$body$
LANGUAGE PLPGSQL;
AI 代码解读

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

5.3.8 字符串连接中变量含有单引号

在下面的示例中,变量param2是一个字符串类型。假设param2的值为adb'-'pg。下面的sql_str直接放到ADB for PG中使用会将-识别成一个operator而报错。需要使用quote_literal函数来进行转换。
示例:

sql_str := 'select * from test1 where col1 = ' || param1 || ' and col2 = '''|| param2 || '''and col3 = 3';
AI 代码解读

转换成:

sql_str := 'select * from test1 where col1 = ' || param1 || ' and col2 = '|| quote_literal(param2) || 'and col3 = 3';
AI 代码解读

5.3.9 获取两个timestamp相减后的天数

示例:

SELECT to_date('2019-06-30 16:16:16') – to_date('2019-06-29 15:15:15') + 1 INTO v_days from dual;
AI 代码解读

转换成:

SELECT extract('days' from '2019-06-30 16:16:16'::timestamp - '2019-06-29 15:15:15'::timestamp + '1 days'::interval)::int INTO v_days;
AI 代码解读

5.4 PL数据类型

5.4.1 Record

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

TYPE rec IS RECORD (a int, b int);
AI 代码解读

改写成:

CREATE TYPE rec AS (a int, b int);
AI 代码解读

5.4.2 Nest table

  1. Nest table 作为pl 变量,可以使用ADB for PG的array类型替换。
    示例:
DECLARE
  TYPE Roster IS TABLE OF VARCHAR2(15);
  names Roster := 
  Roster('D Caruso', 'J Hamil', 'D Piro', 'R Singh');
BEGIN
  FOR i IN names.FIRST .. names.LAST
  LOOP
      IF names(i) = 'J Hamil' THEN
        DBMS_OUTPUT.PUT_LINE(names(i));
      END IF;
  END LOOP;
END;
AI 代码解读

改写成:

create or replace function f1() returns void as 
$$

declare
    names varchar(15)[] := '{"D Caruso", "J Hamil", "D Piro", "R Singh"}';
    len int := array_length(names, 1);
begin
    for i in 1..len loop
        if names[i] = 'J Hamil' then
            raise notice '%', names[i];
        end if;
    end loop;
    return ;
end

$$
 language 'plpgsql';

select f();
AI 代码解读
  1. 作为function返回值,则可以使用table function替换,参考1.3.5节。

5.4.3 Associative Array

无替换类型。

5.4.4 Variable-Size Arrays

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

5.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, nullnull);

CREATE OR REPLACE FUNCTION get_variable() returns setof global_variables AS

$$

DECLARE
    rec global_variables%rowtype;
BEGIN
    execute '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

$$

BEGIN
    execute 'update global_variables set ' ||  quote_ident(param) || ' = ' || quote_literal(value);
END;

$$
 LANGUAGE plpgsql;
AI 代码解读

其中,临时表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;

5.5 SQL

5.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');
AI 代码解读
select emp_id,lead_id,emp_name,prior emp_name as lead_name,salary
     from employee
     start with  lead_id=0
     connect by prior emp_id =  lead_id
AI 代码解读

转换成:

create or replace function f1(tablename text, lead_id int, nocycle boolean) returns setof employee as 
$$

declare
    idx 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;
begin
    
    execute '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 then
            current_result_sql := current_result_sql || ', ' || prev_tbl || ' t2 where t1.lead_id = t2.emp_id';
        else
            current_result_sql := current_result_sql || ' where t1.lead_id = ' || lead_id;
        end if;
        execute current_result_sql;

        -- 如果有环,删除已经遍历过的数据
        if nocycle is false then
            execute '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 loop
        return next rec;
    end loop;
    return;
end

$$
 language plpgsql;
AI 代码解读

5.5.2 Rownum

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

转换成:

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

select rownum, * from t;
转换成:

select row_number() over() as rownum, * from t;
AI 代码解读

5.5.3 Dual表

  1. 去掉dual
    示例:
select sysdate from dual;
AI 代码解读

转换成:

select current_timestamp;
AI 代码解读
  1. 创建一个叫dual的表。

5.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 IS
    v int;
BEGIN
    select b into v from t2 where a = arg;
    return v;
END;

select a, f1(b) from t1;
AI 代码解读

转换成:

select t1.a, t2.b from t1, t2 where t1.b = t2.a;
AI 代码解读

5.5.5 (+)多表外链接

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

oracle
select * from a,b where a.id=b.id(+)
AI 代码解读

转换成:

select * from a left join b on a.id=b.id
AI 代码解读

如果在(+)中有三表的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);
AI 代码解读

转换成:

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);
AI 代码解读

5.5.6 Merge into

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

MERGE INTO test1 t1
            USING (SELECT t2.col1 col1, t3.col2 col2,
                     FROM test2 t2, test3 t3) S
            ON S.col1 = 1 and S.col2 = 2            
WHEN MATCHED THEN
              UPDATE
              SET test1.col1 = S.col1+1,
                     test1.col2 = S.col2+2
WHEN NOT MATCHED THEN
              INSERT (col1, col2)
              VALUES
                (S.col1+1, S.col2+2);
AI 代码解读

转换成:

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 then
    insert into test1 values(test2.col1+1, test3.col2+2);
end if;
AI 代码解读

5.5.7 Sequence

示例:

create sequence seq1; 
select seq1.nextval from dual;
AI 代码解读

转换成:

create SEQUENCE seq1;
select nextval('seq1');
AI 代码解读

5.5.8 Cursor的使用

  • 在oracle中,可以使用下面的语句对cursor进行遍历
    示例:
FUNCTION test_func() IS
    Cursor data_cursor IS SELECT * from test1;
BEGIN
    FOR I IN data_cursor LOOP
        Do something with I;
END LOOP;
END;
AI 代码解读

转换成:

CREATE OR REPLACE FUNCTION test_func()
AS $body$
DECLARE
data_cursor cursor for select * from test1;
I record;
BEGIN
    Open data_cursor;
    LOOP
       Fetch data_cursor INTO I;
      If not found then
            Exit;
      End if;
      Do something with I;
    END LOOP;
    Close data_cursor;
END;
$body$
LANGUAGE PLPGSQL;
AI 代码解读
  • ORACLE可以在递归调用的函数里,重复打开名字相同的cursor。但是在ADB for PG中,则不允许,需要改写成 for I in query的形式。
    示例:
FUNCTION test_func(level IN numer) IS
    Cursor data_cursor IS SELECT * from test1;
BEGIN
If level > 5 then
        return;
   End if;

    FOR I IN data_cursor LOOP
        Do something with I;
        test_func(level + 1);
END LOOP;
END;
AI 代码解读

转换成:

CREATE OR REPLACE FUNCTION test_func(level int) returns void
AS $body$
DECLARE
data_cursor cursor for select * from test1;
I record;
BEGIN
    If level > 5 then
        return;
    End if;
    For I in select * from test1 LOOP
      Do something with I;
       PERFORM test_func(level+1);
    END LOOP;
END;
$body$
LANGUAGE PLPGSQL;
AI 代码解读
相关实践学习
AnalyticDB MySQL海量数据秒级分析体验
快速上手AnalyticDB MySQL,玩转SQL开发等功能!本教程介绍如何在AnalyticDB MySQL中,一键加载内置数据集,并基于自动生成的查询脚本,运行复杂查询语句,秒级生成查询结果。
阿里云云原生数据仓库AnalyticDB MySQL版 使用教程
云原生数据仓库AnalyticDB MySQL版是一种支持高并发低延时查询的新一代云原生数据仓库,高度兼容MySQL协议以及SQL:92、SQL:99、SQL:2003标准,可以对海量数据进行即时的多维分析透视和业务探索,快速构建企业云上数据仓库。 了解产品 https://www.aliyun.com/product/ApsaraDB/ads
目录
打赏
0
0
0
0
39
分享
相关文章
【YashanDB知识库】Kettle迁移PostgreSQL到YashanDB
本文介绍了在Windows环境下使用开源工具Kettle将PostgreSQL数据迁移到YashanDB的方法,适用于YMP不支持PostgreSQL的场景。环境配置包括Kettle 8.3、JAVA 1.8、PostgreSQL 12和YashanDB 23.2.1.100。通过设置JAVA环境变量、解压作业包、启动Kettle图形界面,配置数据库连接(PostgreSQLInput与YashanOutput)以及修改表清单文件等步骤,最终执行总任务完成数据迁移。若迁移失败,可通过日志定位问题并重试,目标表会在每次同步前被truncate以避免数据冲突。
【YashanDB知识库】Kettle迁移PostgreSQL到YashanDB
【YashanDB知识库】共享利用Python脚本解决Oracle的SQL脚本@@用法
【YashanDB知识库】共享利用Python脚本解决Oracle的SQL脚本@@用法
【YashanDB知识库】yashandb执行包含带oracle dblink表的sql时性能差
【YashanDB知识库】yashandb执行包含带oracle dblink表的sql时性能差
【YashanDB 知识库】从 PostgreSQL 迁移到 YashanDB 如何进行数据行数比对
【YashanDB 知识库】从 PostgreSQL 迁移到 YashanDB 如何进行数据行数比对
【YashanDB知识库】从PostgreSQL迁移到YashanDB如何进行数据行数比对
本文介绍了通过Oracle视图`vsqlvsql_plan`分析SQL性能的方法。首先,可通过`plan_hash_value`从`v$sql_plan`获取SQL执行计划,结合示例展示了具体查询方式。文章还创建了一个UDF函数`REPEAT`用于格式化输出,便于阅读复杂执行计划。最后,通过实例展示了如何根据`plan_hash_value`获取SQL文本及其内存中的执行计划,帮助优化性能问题。
【YashanDB知识库】共享利用Python脚本解决Oracle的SQL脚本@@用法
本文来自YashanDB官网,介绍如何处理Oracle客户端sql*plus中使用@@调用同级目录SQL脚本的场景。崖山数据库23.2.x.100已支持@@用法,但旧版本可通过Python脚本批量重写SQL文件,将@@替换为绝对路径。文章通过Oracle示例展示了具体用法,并提供Python脚本实现自动化处理,最后调整批处理脚本以适配YashanDB运行环境。
云原生数据仓库AnalyticDB PostgreSQL同一个SQL可以实现向量索引、全文索引GIN、普通索引BTREE混合查询,简化业务实现逻辑、提升查询性能
本文档介绍了如何在AnalyticDB for PostgreSQL中创建表、向量索引及混合检索的实现步骤。主要内容包括:创建`articles`表并设置向量存储格式,创建ANN向量索引,为表增加`username`和`time`列,建立BTREE索引和GIN全文检索索引,并展示了查询结果。参考文档提供了详细的SQL语句和配置说明。
53 1
如何在 Oracle 中配置和使用 SQL Profiles 来优化查询性能?
在 Oracle 数据库中,SQL Profiles 是优化查询性能的工具,通过提供额外统计信息帮助生成更有效的执行计划。配置和使用步骤包括:1. 启用自动 SQL 调优;2. 手动创建 SQL Profile,涉及收集、执行调优任务、查看报告及应用建议;3. 验证效果;4. 使用 `DBA_SQL_PROFILES` 视图管理 Profile。
|
5月前
|
如何应用SQL约束条件?
【10月更文挑战第28天】如何应用SQL约束条件?
155 11
|
6月前
|
SQL
数仓规范之sql编写规范
编写SQL时,应遵循以下规范:所有关键字小写,表别名按a, b, c...顺序使用,复杂逻辑多行书写,提高可读性。SELECT字段需逐行列出,避免使用*,GROUP BY字段同样处理。WHERE条件多于一个时,每条件一行。JOIN子表推荐使用嵌套查询方式1,明确关联条件,避免笛卡尔积。关键逻辑需注释,INSERT SELECT后最外层字段加注释说明用途。示例中展示了推荐的JOIN替代子查询的写法,以提高代码的可读性和维护性。
258 1

热门文章

最新文章

相关产品

  • 云数据库 RDS PostgreSQL 版
  • 推荐镜像

    更多