4.4. PLSQL

本节主要介绍统一SQL支持原生Oracle数据库中的PLSQL

4.4.1. 数据类型

描述
PL 中每个常量、变量、参数、函数返回值都有一个数据类型,用来定义存储格式、使用限制、有效值范围以及合法操作等。
分类
  • SQL 数据类型

  • 布尔型

  • RECOED

  • PLS_INTEGER

  • %TYPE

  • 联合数组

  • 嵌套表

  • 可变数组

  • 自定义子类型

  • 游标指针

4.4.1.1. SQL 数据类型

请参考Oracle2LightDB-Oracle中数据类型:

数据类型

警告

DECLARE 中变量有如下限制:
  • CONSTANT 暂不支持

  • XMLTYPE 暂不支持赋值

  • INTERVAL YEAR TO MONTH 暂不支持赋值

  • INTERVAL YEAR(p) TO MONTH 暂不支持赋值

  • INTERVAL DAY TO SECOND 暂不支持赋值

  • INTERVAL DAY(p) TO SECOND(s) 暂不支持赋值

  • TIMESTAMP 暂不支持赋值

  • TIMESTAMP WITH TIME ZONE 暂不支持赋值

  • TIMESTAMP(p) WITH TIME ZONE 暂不支持赋值

  • 赋值暂时只支持简单表达式

  • 长度、精度、标度暂时不做范围限制,具体是否错误由执行层保证

示例

-- 转换前Oracle SQL:
DECLARE
   acct_id1 BINARY_FLOAT := 0.0;
   acct_id2 BINARY_DOUBLE := 0.0;
   acct_id3 ROWID := '(0,1)';
   acct_id4 BLOB := EMPTY_BLOB();
   acct_id5 CHAR := 'A';
   acct_id6 CHARACTER := 'A';
   acct_id7 CLOB := EMPTY_CLOB();
   acct_id8 DATE := TO_DATE('2024-03-19', 'YYYY-MM-DD');
   acct_id9 DECIMAL := 0;
   acct_id10 DEC := 0;
   acct_id12 DOUBLE PRECISION := 0.0;
   acct_id13 FLOAT := 0.0;
   acct_id14 INTEGER := 0;
   acct_id15 INT := 0;
   acct_id16 INTERVAL YEAR TO MONTH ;
   acct_id17 INTERVAL YEAR(2) TO MONTH ;
   acct_id18 INTERVAL DAY TO SECOND ;
   acct_id19 INTERVAL DAY(1) TO SECOND(6) ;
   acct_id20 LONG := 'Sample text';
   acct_id21 LONG RAW := utl_raw.cast_to_raw('Sample text');
   acct_id22 NCHAR := 'A';
   acct_id23 NCHAR VARYING(2) := 'AA';
   acct_id24 NCLOB := EMPTY_CLOB();
   acct_id25 NUMBER := 9999;
   acct_id26 NUMERIC := 0;
   acct_id27 NVARCHAR2(2000) := 'Initial value';
   acct_id28 RAW(2000) := hextoraw('0102030405060708');
   acct_id29 REAL := 0.0;
   acct_id31 SMALLINT := 0;
   acct_id32 TIMESTAMP := TO_TIMESTAMP('2024-03-19 12:34:56', 'YYYY-MM-DD HH24:MI:SS');
   acct_id33 TIMESTAMP WITH TIME ZONE ;
   acct_id34 TIMESTAMP(3) WITH TIME ZONE ;
   acct_id35 UROWID := 'AAAACPAABAAAAShAAA';
   acct_id36 VARCHAR(51) := 'NULL';
   acct_id37 VARCHAR2(151) := 'NULL';
   acct_id38 XMLTYPE ;

BEGIN
   acct_id25 := 9999;
END;


-- 转换后LightDB-Oracle SQL:
DECLARE
   acct_id1 binary_float := 0.0;
   acct_id2 binary_double := 0.0;
   acct_id3 rowid := '(0,1)';
   acct_id4 blob := EMPTY_BLOB();
   acct_id5 char := 'A';
   acct_id6 character := 'A';
   acct_id7 clob := EMPTY_CLOB();
   acct_id8 date := TO_DATE('2024-03-19', 'YYYY-MM-DD');
   acct_id9 decimal(38,0) := 0;
   acct_id10 dec(38,0) := 0;
   acct_id12 double precision := 0.0;
   acct_id13 float := 0.0;
   acct_id14 bigint := 0;
   acct_id15 bigint := 0;
   acct_id16 interval year to month;
   acct_id17 interval year to month;
   acct_id18 interval day to second;
   acct_id19 interval day to second(6);
   acct_id20 long := 'Sample text';
   acct_id21 bytea := utl_raw.cast_to_raw('Sample text');
   acct_id22 nchar := 'A';
   acct_id23 nchar varying(2) := 'AA';
   acct_id24 clob := EMPTY_CLOB();
   acct_id25 number := 9999;
   acct_id26 numeric(38,0) := 0;
   acct_id27 nvarchar2(2000) := 'Initial value';
   acct_id28 raw(2000) := hextoraw('0102030405060708');
   acct_id29 real := 0.0;
   acct_id31 bigint := 0;
   acct_id32 timestamp := TO_TIMESTAMP('2024-03-19 12:34:56', 'YYYY-MM-DD HH24:MI:SS');
   acct_id33 timestamp with time zone;
   acct_id34 timestamp(3) with time zone;
   acct_id35 varchar(4000) := 'AAAACPAABAAAAShAAA';
   acct_id36 varchar(51) := 'NULL';
   acct_id37 varchar2(151) := 'NULL';
   acct_id38 xmltype;
BEGIN
   acct_id25 := 9999;
END;

4.4.1.2. 布尔型

语法

variable_name BOOLEAN

示例

-- 转换前Oracle SQL:
DECLARE
   acct_id1 BOOLEAN;
   acct_id2 BOOLEAN := FALSE;
BEGIN
   acct_id1 := true;
   acct_id2 := true;
END;


-- 转换后LightDB-Oracle SQL:
DECLARE
   acct_id1 boolean;
   acct_id2 boolean := FALSE;
BEGIN
   acct_id1 := TRUE;
   acct_id2 := TRUE;
END;

4.4.1.3. RECOED

语法

TYPE record_type IS RECORD ( field_definition [, field_definition]... ) ;

警告

field_definition暂时只支持field datatype

示例

-- 转换前Oracle SQL:
DECLARE
type recordType is record(
   age integer,
   tal integer);
record1 recordType;

BEGIN
record1.age:=1;
END;


-- 转换后LightDB-Oracle SQL:
DECLARE
   TYPE recordType IS RECORD(
      age bigint,
      tal bigint
   );
   record1 recordType;
BEGIN
   record1.age := 1;
END;

4.4.1.4. PLS_INTEGER

语法

variable_name PLS_INTEGER

示例

-- 转换前Oracle SQL:
DECLARE
   acct_id40 pls_integer:=-123;
BEGIN
   acct_id40 := 9999;
END;


-- 转换后LightDB-Oracle SQL:
DECLARE
   acct_id40 pls_integer := -123;
BEGIN
   acct_id40 := 9999;
END;

4.4.1.5. %TYPE

语法

variable_name table_or_view_name.column_name%TYPE;

示例

-- 转换前Oracle SQL:
CREATE TABLE te(a int);
DECLARE
   id te.a%TYPE;
BEGIN
id := 9999;
END;


-- 转换后LightDB-Oracle SQL:
CREATE TABLE te (a bigint);
DECLARE
   id te.a%TYPE;
BEGIN
   id := 9999;
END;

4.4.1.6. 联合数组

语法

TYPE typeName IS TABLE OF datetype INDEX BY { PLS_INTEGER | BINARY_INTEGER| VARCHAR2(size)};

警告

暂时只支持定义解析

示例

-- 转换前Oracle SQL:
DECLARE
   TYPE typeName1 IS TABLE OF VARCHAR2(10) INDEX BY PLS_INTEGER;
   TYPE typeName2 IS TABLE OF VARCHAR2(10) INDEX BY BINARY_INTEGER;
   TYPE typeName3 IS TABLE OF VARCHAR2(10) INDEX BY VARCHAR2(10);
   v1 typeName1;
   v2 typeName2;
   v3 typeName3;
   v4 int;
BEGIN
   v4:=999;
END;


-- 转换后LightDB-Oracle SQL:
DECLARE
   TYPE typeName1 IS TABLE OF varchar2(10) INDEX BY pls_integer;
   TYPE typeName2 IS TABLE OF varchar2(10) INDEX BY binary_integer;
   TYPE typeName3 IS TABLE OF varchar2(10) INDEX BY varchar2(10);
   v1 typeName1;
   v2 typeName2;
   v3 typeName3;
   v4 bigint;
BEGIN
   v4 := 999;
END;

4.4.1.7. 嵌套表

语法

TYPE typeName IS TABLE OF datetype [NOT NULL];

警告

暂时只支持定义解析

示例

-- 转换前Oracle SQL:
DECLARE
   TYPE typeName IS TABLE OF VARCHAR2(10);
   v1 typeName;
   v2 int;
BEGIN
   v2:=999;
END;


-- 转换后LightDB-Oracle SQL:
DECLARE
   TYPE typeName IS TABLE OF varchar2(10);
   v1 typeName;
   v2 bigint;
BEGIN
   v2 := 999;
END;

4.4.1.8. 可变数组

语法

TYPE typeName IS VARRAY(size) OF datetype [NOT NULL];

警告

暂时只支持定义解析

示例

-- 转换前Oracle SQL:
DECLARE
   TYPE typeName IS VARRAY(2) OF int;
   v1 typeName;
   v2 int;
BEGIN
   v2:=999;
END;


-- 转换后LightDB-Oracle SQL:
DECLARE
   TYPE typeName IS VARRAY(2) OF bigint;
   v1 typeName;
   v2 bigint;
BEGIN
   v2 := 999;
END;

4.4.1.9. 自定义子类型

警告

暂不支持

4.4.1.10. 游标指针

警告

暂不支持

4.4.2. 存储过程和函数

描述
存储过程 (Procedure)和函数(Function)统称为 PL/SQL 程序,它们被命名为 PL/SQL 块,并通过输入输出参数与调用者交换信息。存储过程和函数的唯一区别,就是函数定义时需要声明一个返回值,函数结束前要返回一个值。

4.4.2.1. 创建存储过程

存储过程是 SQL 语句和可选控制流语句的预编译集合,PL/SQL 将其作为一个单元进行处理。一个存储过程可以引用其它存储过程,并且可以返回多个变量。

语法

CREATE [ OR REPLACE ] [ EDITIONABLE | NONEDITIONABLE ] PROCEDURE
[ schema. ] procedure_name
[ ( parameter_declaration [, parameter_declaration ]... ) ]
{ IS | AS } { [ declare_section ] body } ;

parameter_declaration :
   parameter [ [ IN ] datatype [ { := | DEFAULT } expression ]
         | { OUT | IN OUT } datatype  ]

declare_section:
   参照PL/SQL数据类型

body:
BEGIN statement ... END [ name ]

参数解释

参数

说明

[schema. ]procedure_name

Schema 指定当前同义词属于哪个用户。 procedure_name 表示存储过程的名称。

parameter_declaration

存储过程的相关声明

declare_section

PL/SQL块的声明部分,参照PL/SQL数据类型

body

PL/SQL块的执行部分,支持BEGIN、END、IF THEN分支,loop循环,变量,赋值,ROLLBACK,COMMIT,RETURN。

示例

-- 转换前Oracle PL/SQL:
CREATE OR REPLACE procedure unisqltradeholder(market_v in char,holder_v in varchar2, fund_v in number, combi_v number default -1,
   c_default_status_p char default '1', combiname_v varchar2 default '0') is
      combi_p number := -1;
      bindseat_v varchar2(10) := '';
      icnt number := 0;
      icnt1 number := 0;
      icnt2 number := 0;
      l_error_code    int := 0;
   begin
      l_error_code := 1;
      combi_p := combi_v;
      l_error_code := 2;
      if combi_v = -1 then
         l_error_code := 3;
      end if;
      icnt := 0;
      l_error_code := 3;
      if (icnt > 0 ) then
         l_error_code := 4;
      end if;
      commit;
   end unisqltradeholder;

-- 转换后LightDB-Oracle PL/SQL:
CREATE OR REPLACE PROCEDURE unisqltradeholder(market_v IN char,holder_v IN varchar2,fund_v IN number,combi_v number DEFAULT -1,
c_default_status_p char DEFAULT '1',combiname_v varchar2 DEFAULT '0')  IS
   combi_p number := -1;
   bindseat_v varchar2(10) := '';
   icnt number := 0;
   icnt1 number := 0;
   icnt2 number := 0;
   l_error_code int := 0;
BEGIN
   l_error_code := 1;
   combi_p := combi_v;
   l_error_code := 2;
   IF combi_v=-1 THEN l_error_code := 3;
END IF;
   icnt := 0;
   l_error_code := 3;
   IF (icnt>0) THEN l_error_code := 4;
END IF;
   COMMIT;END unisqltradeholder;

4.4.2.2. 创建函数

PL/SQL 函数是由一个或多个 SQL 语句组成的子程序,可重复执行。函数只能返回一个变量,并且必须包含一个有效的 RETURN 语句。

语法

CREATE [ OR REPLACE ] [ EDITIONABLE | NONEDITIONABLE ] FUNCTION
[ schema. ] function_name
[ ( parameter_declaration [, parameter_declaration ]... ) ] RETURN datatype
{ IS | AS } { [ declare_section ] body } ;

parameter_declaration :
   parameter [ [ IN ] datatype [ { := | DEFAULT } expression ]
         | { OUT | IN OUT } datatype  ]

declare_section:
   参照PL/SQL数据类型

body:
BEGIN statement ... END [ name ]

参数解释

参数

说明

[schema. ]function_name

Schema 指定当前同义词属于哪个用户。 function_name 表示函数的名称。

parameter_declaration

函数的相关声明

declare_section

PL/SQL块的声明部分,参照PL/SQL数据类型

body

PL/SQL块的执行部分,支持BEGIN、END、IF THEN分支,loop循环,变量,赋值(:=),ROLLBACK,COMMIT,RETURN。

示例

-- 转换前Oracle PL/SQL:
CREATE OR REPLACE FUNCTION return_input(
 input_value IN NUMBER
) RETURN NUMBER IS
   sum_result NUMBER := 0;
BEGIN
   FOR i IN 0 .. input_value LOOP
      sum_result := sum_result + i;
   END LOOP;
   RETURN sum_result;
END;

-- 转换后LightDB-Oracle PL/SQL:
CREATE OR REPLACE FUNCTION return_input(input_value IN number) RETURN number IS
   sum_result number := 0;
BEGIN
   FOR i IN 0 .. input_value LOOP sum_result := sum_result+i;
END  LOOP;
   RETURN sum_result;
END;

警告

下面列举的是在使用统一SQL的PL/SQL时的部分疑问点

  • Oracle的PL/SQL和LightDB PL/SQL存在一定的差异,本版本未强制转化,以Oracle的PL/SQL为输出,后续LightDB版本支持之后,会逐步转化迭代。

  • PL/SQL块的执行部分,支持BEGIN、END、IF THEN分支,loop循环,变量,赋值(:=),ROLLBACK,COMMIT,RETURN,但存在限制,loop循环的循环变量上、下界前后需要添加空格,仅支持简单默认的循环,不支持REVERSE。

  • PL/SQL语法是否正确不考虑,具体是否错误由执行层保证。

  • 支持范围仅考虑数据类型,本章节目的仅在展示支持PL/SQL的创建存储过程和函数的框架功能。