3.2.11.3. GaussDB-Oracle
3.2.11.3.1. PL/SQL 匿名块
描述
程序由三个块组成,即声明部分(DECLARE)、执行部分(BEGIN)和异常处理部分(EXCEPTION)。
三个部分的作用如下:
声明部分:在此声明 PL/SQL 用到的变量、常量、游标、类型等。
执行部分:包含程序的主要逻辑,执行SQL操作,控制结构和其他语句。
异常处理部分:捕获和处理在执行部分出现的异常。
语法
[DECLARE]
-- 声明部分 (可选)
BEGIN
-- 执行部分: 过程及 SQL 语句,即程序的主要部分
[EXCEPTION]
-- 异常处理部分: 错误处理
END;
警告
不支持匿名块带标签名称。
示例
-- 转换前Oracle :
DECLARE
v int;
BEGIN
NULL;
EXCEPTION
WHEN OTHERS THEN
NULL;
END;
-- 转换后GaussDB-Oracle 匿名块:
DECLARE
v bigint;
BEGIN
NULL;
EXCEPTION
WHEN others THEN
NULL;
END;
3.2.11.3.2. 数据类型
基础类型转换 参见 数据类型转换映射表
PLSQL内置数据类型
表名.列%type --> 表名.列%type
表名%rowtype --> 表名%rowtype
警告
CHAR(N)
CHARACTER(N)
NCHAR VARYING(N)
NVARCHAR2(N)
RAW(N)
N最大支持2000 。NCHAR(N)
N 最大支持1000 。DECIMAL(p,s)
DEC(p,s)
NUMBER(p,s)
NUMERIC(p,s)
p和s最大支持38 。FLOAT(N)
最大支持53 。
3.2.11.3.3. 声明
语法
variable_name [CONSTANT] datatype [NOT NULL] [:=|DEFAULT expression]
警告
不支持出现重名的变量
示例
-- 转换前Oracle:
DECLARE
c_MAX_SALARY CONSTANT NUMBER := 10000;
c_DEFAULT_NAME CONSTANT VARCHAR2(50) := 'Default Name';
v11 NUMBER := 100;
BEGIN
NULL;
END;
-- 转换后GaussDB-Oracle:
DECLARE
c_max_salary CONSTANT number := 10000;
c_default_name CONSTANT varchar2(50) := 'Default Name';
v11 number := 100;
BEGIN
NULL;
END;
3.2.11.3.4. 条件控制
3.2.11.3.4.1. IF
语法
IF condition_1 THEN
statements_1
[ ELSIF condition_2 THEN
statements_2
]
[ ELSIF condition_3 THEN
statements_3
]...
[ ELSE
else_statements
]
END IF;
示例
-- 转换前Oracle:
DECLARE
grade CHAR(1);
msg VARCHAR(200);
BEGIN
grade := 'B';
if grade = 'A' THEN
msg := 'Excellent';
ELSIF grade = 'B' THEN
msg := 'Very Good';
ELSIF grade = 'C' THEN
msg := 'Good';
ELSIF grade = 'D' THEN
msg := 'Fair';
ELSIF grade = 'F' THEN
msg := 'Poor';
ELSE
msg := 'No such grade';
END IF;
END;
-- 转换后GaussDB-Oracle:
DECLARE
grade char(1);
msg varchar(200);
BEGIN
grade := 'B';
IF grade='A' THEN
msg := 'Excellent';
ELSIF grade='B' THEN
msg := 'Very Good';
ELSIF grade='C' THEN
msg := 'Good';
ELSIF grade='D' THEN
msg := 'Fair';
ELSIF grade='F' THEN
msg := 'Poor';
ELSE
msg := 'No such grade';
END IF;
END;
3.2.11.3.4.2. CASE 语句
语法
CASE selector
WHEN selector_value_1 THEN statements_1
WHEN selector_value_2 THEN statements_2
...
WHEN selector_value_n THEN statements_n
[ ELSE
else_statements ]
END CASE;
CASE
WHEN condition_1 THEN statements_1
WHEN condition_2 THEN statements_2
...
WHEN condition_n THEN statements_n
[ ELSE
else_statements ]
END CASE;
示例
-- 转换前Oracle:
DECLARE
grade CHAR(1);
msg varchar(200);
BEGIN
grade := 'B';
CASE grade
WHEN 'A' THEN
msg := 'Excellent';
WHEN 'B' THEN
msg := 'Very Good';
WHEN 'C' THEN
msg := 'Good';
WHEN 'D' THEN
msg := 'Fair';
WHEN 'F' THEN
msg := 'Poor';
ELSE
msg := 'No such grade';
END CASE;
END;
-- 转换后GaussDB-Oracle:
DECLARE
grade char(1);
msg varchar(200);
BEGIN
grade := 'B';
CASE grade
WHEN 'A' THEN
msg := 'Excellent';
WHEN 'B' THEN
msg := 'Very Good';
WHEN 'C' THEN
msg := 'Good';
WHEN 'D' THEN
msg := 'Fair';
WHEN 'F' THEN
msg := 'Poor';
ELSE
msg := 'No such grade';
END CASE;
END;
3.2.11.3.5. 循环控制语句
3.2.11.3.5.1. LOOP
LOOP
statements
END LOOP;
示例
-- 转换前Oracle :
DECLARE
v_counter NUMBER := 1;
BEGIN
LOOP
v_counter := v_counter + 1;
EXIT WHEN v_counter > 3;
END LOOP;
END;
-- 转换后GaussDB-Oracle:
DECLARE
v_counter number := 1;
BEGIN
LOOP
v_counter := v_counter+1;
EXIT WHEN v_counter>3;
END LOOP;
END;
3.2.11.3.5.2. FOR LOOP
FOR variable IN [REVERSE] lower_bound..upper_bound LOOP
statements
END LOOP;
警告
REVERSE开启后,转换会对lower_bound和upper_bound交换位置来确保语义一致。
不支持FOR LOOP 接游标的用法。
示例
-- 转换前Oracle :
DECLARE
first INTEGER := 1;
last INTEGER := 10;
high INTEGER := 100;
low INTEGER := 12;
BEGIN
FOR j IN -5..5 LOOP
log_info (j);
END LOOP;
FOR k IN REVERSE first..last LOOP
log_info (k);
END LOOP;
FOR step IN 0..(TRUNC(high/low) * 2) LOOP
log_info (step);
END LOOP;
END;
-- 转换后GaussDB-Oracle:
DECLARE
first bigint := 1;
last bigint := 10;
high bigint := 100;
low bigint := 12;
BEGIN
FOR j IN -5..5 LOOP
log_info(j) ;
END LOOP;
FOR k IN REVERSE last..first LOOP
log_info(k) ;
END LOOP;
FOR step IN 0..(TRUNC(high/unisql.null_if_zero(low))*2) LOOP
log_info(step) ;
END LOOP;
END;
3.2.11.3.5.3. WHILE LOOP
WHILE condition LOOP
statements
END LOOP;
示例
-- 转换前Oracle :
DECLARE
v_counter NUMBER := 1;
BEGIN
WHILE v_counter <= 3 LOOP
log_info('Counter value in WHILE loop: ' || v_counter);
v_counter := v_counter + 1;
END LOOP;
END;
-- 转换后GaussDB-Oracle:
DECLARE
v_counter number := 1;
BEGIN
WHILE v_counter<=3 LOOP
log_info('Counter value in WHILE loop: ' || v_counter) ;
v_counter := v_counter+1;
END LOOP;
END;
3.2.11.3.6. 注释
单行注释和多行注释转换时,会被丢掉,不影响程序的执行。
语法
BEGIN
-- 单行注释
/*
多行注释
*/
END;
警告
转换后注释将会被丢弃
示例
-- 转换前Oracle:
DECLARE
v date;
BEGIN
-- 这是单行注释
/*
这是多行注释
示例e.g SELECT CURRENT_DATE INTO v FROM dual
*/
SELECT CURRENT_DATE INTO v FROM dual /*这是测试*/ ;
END;
-- 转换后GaussDB-Oracle:
DECLARE
v date;
BEGIN
SELECT localtimestamp(0) INTO v FROM sys_dummy;
END;
3.2.11.3.7. 游标
语法
声明游标:
CURSOR cursor_name(argName type[%type][,...]) IS select_statement;
打开:
OPEN cur_name(arg [, ...]);
获取游标:
FETCH cur_name INTO variable [,...];
关闭游标:
CLOSE cursor_name;
警告
不支持动态游标,循环游标。
示例
-- 转换前Oracle:
DECLARE
-- 1. 声明游标
CURSOR cur_employees IS
SELECT employee_id, name, salary
FROM employees_20250707
WHERE salary >= 5000 ORDER BY employee_id asc;
-- 定义变量存储游标数据
v_emp_id NUMBER;
v_emp_name VARCHAR2(50);
v_salary NUMBER;
BEGIN
-- 2. 打开游标
OPEN cur_employees;
-- 3. 循环提取数据
LOOP
FETCH cur_employees INTO v_emp_id, v_emp_name, v_salary;
EXIT WHEN cur_employees%NOTFOUND; -- 游标属性判断是否结束
log_info(
'Employee ID:' || v_emp_id ||
', Name:' || v_emp_name ||
', Salary:' || v_salary
);
END LOOP;
-- 4. 关闭游标
CLOSE cur_employees;
END;
-- 转换后GaussDB-Oracle:
DECLARE
CURSOR cur_employees IS SELECT employee_id,name,salary FROM employees_20250707 WHERE salary>=5000 ORDER BY employee_id;
v_emp_id number;
v_emp_name varchar2(50);
v_salary number;
BEGIN
OPEN cur_employees;
LOOP
FETCH cur_employees INTO v_emp_id, v_emp_name, v_salary;
EXIT WHEN cur_employees%NOTFOUND;
log_info('Employee ID:' || v_emp_id ||
', Name:' || v_emp_name ||
', Salary:' || v_salary
) ;
END LOOP;
CLOSE cur_employees;
END;
3.2.11.3.8. 静态SQL
语法
SELECT ...
INSERT ...
DELETE ...
UPDATE ...
警告
在匿名块中,GaussDB_Oracle的事务管理和Oracle存在差异,GaussDB_Oracle中发生异常后会对前面的操作进行回滚,而Oracle会进行语句级提交。
统一SQL的语句级别开关
/*+SkipTransform*/
/*+noSkipTransform*/
在PL/SQL中未做支持。
示例
-- 转换前Oracle:
DECLARE
v_name varchar(64);
v_bonus NUMBER(8,2);
v_employee_id NUMBER(8,2) default 100;
BEGIN
INSERT INTO employees_20250707 (employee_id, name, salary, hire_date) VALUES (100, 'Bob', 5000, TO_DATE('2021-02-15', 'YYYY-MM-DD'));
SELECT salary * 0.10, name INTO v_bonus, v_name FROM employees_20250707 WHERE employee_id = 100;
log_info('name = ' || v_name || ' bonus = ' || TO_CHAR(v_bonus));
UPDATE employees_20250707 SET salary = salary + 1000 WHERE employee_id = v_employee_id;
SELECT salary * 0.10, name INTO v_bonus, v_name FROM employees_20250707 WHERE employee_id = 100;
log_info('name = ' || v_name || ' bonus = ' || TO_CHAR(v_bonus));
DELETE FROM employees_20250707 WHERE employee_id = v_employee_id;
END;
-- 转换后GaussDB-Oracle:
DECLARE
v_name varchar(64);
v_bonus number(8,2);
v_employee_id number(8,2) := 100;
BEGIN
INSERT INTO employees_20250707 (employee_id,name,salary,hire_date) VALUES (100,'Bob',5000,TO_DATE('2021-02-15', 'YYYY-MM-DD'));
SELECT salary*0.10,name INTO v_bonus, v_name FROM employees_20250707 WHERE employee_id=100;
log_info('name = ' || v_name || ' bonus = ' || TO_CHAR(v_bonus)) ;
UPDATE employees_20250707 SET salary=salary+1000 WHERE employee_id=v_employee_id;
SELECT salary*0.10,name INTO v_bonus, v_name FROM employees_20250707 WHERE employee_id=100;
log_info('name = ' || v_name || ' bonus = ' || TO_CHAR(v_bonus)) ;
DELETE FROM employees_20250707 WHERE employee_id=v_employee_id;
END;
3.2.11.3.9. 动态SQL
语法
EXECUTE IMMEDIATE dynamic_sql_string
[INTO variable [,....]]
[USING bind_arg [,...]]
警告
当且仅当
dynamic_sql_string
为字符串文本时,会对dynamic_sql_string
进行SQL语句转换,其他情况(如变量,表达式)只会透传。当
dynamic_sql_string
使用Q语法(不支持多字节的Q语法)包裹字符串时,转后会改写为单引号的形式。USING
bind_arg
未支持指定OUT/IN修饰关键字。USING
bind_arg
源库和目标库存在差异,当且仅当dynamic_sql_string
绑定参数不重复使用并且与USING
指定的绑定参数个数完全一致并且dynamic_sql_string
转换后参数不会拓展成多个,则两个库行为一致,其他情况不支持,见示例。
示例
-- 错误示例
-- 转换前Oracle:
-- 取 v_employee_id , v_name , v_salary , v_department_id 值插入数据库
DECLARE
v_employee_id number := 1;
v_name varchar2(50) := 'Bob Brown';
v_salary number := 55000;
v_department_id NUMBER DEFAULT 10;
BEGIN
EXECUTE IMMEDIATE q'[DELETE FROM employees_20250707]';
EXECUTE IMMEDIATE 'INSERT INTO employees_20250707 (employee_id,name,salary,department_id) VALUES (:1,:2,:3,:1)' USING v_employee_id, v_name, v_salary, v_department_id;
END;
-- 转换后GaussDB-Oracle:
-- 取 v_employee_id , v_name , v_salary , v_employee_id 值插入数据库
DECLARE
v_employee_id number := 1;
v_name varchar2(50) := 'Bob Brown';
v_salary number := 55000;
v_department_id number := 10;
BEGIN
EXECUTE IMMEDIATE 'DELETE FROM employees_20250707';
EXECUTE IMMEDIATE 'INSERT INTO employees_20250707 (employee_id,name,salary,department_id) VALUES (:1,:2,:3,:1)' USING v_employee_id, v_name, v_salary, v_department_id;
END;
-- 正确示例
-- 转换前Oracle:
-- 取 v_employee_id , v_name , v_salary , v_department_id 值插入数据库
DECLARE
v_employee_id number := 1;
v_name varchar2(50) := 'Bob Brown';
v_salary number := 55000;
v_department_id NUMBER DEFAULT 10;
BEGIN
EXECUTE IMMEDIATE q'[DELETE FROM employees_20250707]';
EXECUTE IMMEDIATE 'INSERT INTO employees_20250707 (employee_id,name,salary,department_id) VALUES (:1,:2,:3,:4)' USING v_employee_id, v_name, v_salary, v_department_id;
END;
-- 转换后GaussDB-Oracle:
-- 取 v_employee_id , v_name , v_salary , v_department_id 值插入数据库
DECLARE
v_employee_id number := 1;
v_name varchar2(50) := 'Bob Brown';
v_salary number := 55000;
v_department_id number := 10;
BEGIN
EXECUTE IMMEDIATE 'DELETE FROM employees_20250707';
EXECUTE IMMEDIATE 'INSERT INTO employees_20250707 (employee_id,name,salary,department_id) VALUES (:1,:2,:3,:4)' USING v_employee_id, v_name, v_salary, v_department_id;
END;
3.2.11.3.10. 异常
语法
-- plsql中异常处理:
EXCEPTION
WHEN ex_name_1 THEN statements_1 -- 异常处理程序
WHEN ex_name_2 OR ex_name_3 THEN statements_2 -- 异常处理程序
WHEN OTHERS THEN statements_3 -- 异常处理程序
END;
exception_name EXCEPTION; -- 自定义异常声明:
PRAGMA EXCEPTION_INIT(exception_name, error_code); -- 异常关联错误码
raise [exception_name]; -- 抛出异常
警告
异常码和异常信息对于Oracle和GaussDB-Oracle并不一致,这部分统一SQL未做支持。
预定义的异常名称
NO_DATA_FOUND
TOO_MANY_ROWS
OTHERS
支持,其他的异常名也是透传但不做支持。Oracle exception_init支持绑定内部错误码,GaussDBOracle未做支持。
示例
-- 转换前Oracle:
DECLARE
-- 自定义异常
custom_exception EXCEPTION;
BEGIN
-- 显式抛出异常
RAISE custom_exception;
EXCEPTION
WHEN custom_exception THEN
DBMS_OUTPUT.PUT_LINE('捕获自定义异常: 业务逻辑错误');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('其他未知错误: ' || SQLERRM);
END;
-- 转换后GaussDB-Oracle:
DECLARE
custom_exception EXCEPTION;
BEGIN
RAISE custom_exception;
EXCEPTION
WHEN custom_exception THEN
dbms_output.put_line('捕获自定义异常: 业务逻辑错误') ;
WHEN others THEN
dbms_output.put_line('其他未知错误: ' || SQLERRM) ;
END;