1.3.2.11.6. DM
1.3.2.11.6.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;
-- 转换后匿名块:
DECLARE
v int;
BEGIN
NULL;
EXCEPTION
WHEN others THEN
NULL;
END;
1.3.2.11.6.2. 数据类型
基础类型转换 参见 数据类型转换映射表
警告
BINARY_FLOAT,BINARY_DOUBLE源库和目标库精度存在差异,以实际目标库为准。如1.3 在Oracle中输出为1.29999995E+000(不同环境可能存在差异),而在DM中输出为1.3E0;DM匿名块不支持
long rawrowidurowidxmltype类型;DM匿名块
nchar(n),nvarchar2(n), n表示的是字节数,不是字符数, 和Oracle不一致;定义的
integer类型变量,会转换成int类型;
1.3.2.11.6.3. 声明
语法
variable_name [CONSTANT] datatype [NOT NULL] [:=|DEFAULT] expression
警告
不支持出现重名的变量;
变量名不能是DM数据库中的关键字;
示例
-- 转换前Oracle:
DECLARE
c_MAX_SALARY NUMBER NOT NULL := 10000;
v11 NUMBER NOT NULL := 100; -- 数值类型,含默认值
v12 INTEGER NOT NULL DEFAULT 200;
-- 常量定义
c_MAX_SALARY1 CONSTANT NUMBER NOT NULL := 10000;
c_MAX_SALARY2 CONSTANT NUMBER DEFAULT 10000;
c_MAX_SALARY3 CONSTANT NUMBER NOT NULL DEFAULT 10000;
c_DEFAULT_NAME CONSTANT VARCHAR2(50) := 'Default Name';
BEGIN
DBMS_OUTPUT.PUT_LINE('c_MAX_SALARY: ' || c_MAX_SALARY);
-- 输出常量
DBMS_OUTPUT.PUT_LINE('c_DEFAULT_NAME: ' || c_DEFAULT_NAME);
END;
-- 转换后:
DECLARE
c_max_salary number NOT NULL := 10000;
v11 number NOT NULL := 100;
v12 int NOT NULL := 200;
c_max_salary1 CONSTANT number NOT NULL := 10000;
c_max_salary2 CONSTANT number := 10000;
c_max_salary3 CONSTANT number NOT NULL := 10000;
c_default_name CONSTANT varchar2(50) := 'Default Name';
BEGIN
dbms_output.put_line('c_MAX_SALARY: ' || c_MAX_SALARY) ;
dbms_output.put_line('c_DEFAULT_NAME: ' || c_DEFAULT_NAME) ;
END;
1.3.2.11.6.4. 条件控制
1.3.2.11.6.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;
-- 转换后:
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;
1.3.2.11.6.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;
-- 转换后:
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;
1.3.2.11.6.5. 循环控制语句
1.3.2.11.6.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;
-- 转换后:
DECLARE
v_counter number := 1;
BEGIN
LOOP
v_counter := v_counter+1;
EXIT WHEN v_counter>3;
END LOOP;
END;
1.3.2.11.6.5.2. FOR LOOP
FOR variable IN [REVERSE] lower_bound..upper_bound LOOP
statements
END LOOP;
示例
-- 转换前Oracle :
DECLARE
first1 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 first1..last LOOP
log_info (k);
END LOOP;
FOR step IN 0..(TRUNC(high/low) * 2) LOOP
log_info (step);
END LOOP;
END;
-- 转换后:
DECLARE
first1 int := 1;
last int := 10;
high int := 100;
low int := 12;
BEGIN
FOR j IN -5..5 LOOP
log_info(j) ;
END LOOP;
FOR k IN REVERSE first1..last LOOP
log_info(k) ;
END LOOP;
FOR step IN 0..(TRUNC(high/low)*2) LOOP
log_info(step) ;
END LOOP;
END;
1.3.2.11.6.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;
-- 转换后:
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;
1.3.2.11.6.5.4. CURSOR FOR
FOR variable IN ( Select ... ) LOOP
statements
END LOOP;
警告
FOR循环IN后面不支持游标名;
示例
-- 转换前Oracle :
DECLARE
/* 自定义变量 */
v_dept_id NUMBER(10);
v_dept_name VARCHAR2(50);
v_location VARCHAR2(50);
v_loop_count NUMBER := 0; /* 统计循环次数 */
BEGIN
/* 单层 FOR SELECT 循环:遍历部门表所有数据 */
FOR dept_rec IN (
SELECT department_id, department_name, location
FROM departments
ORDER BY department_id
) LOOP
/* 直接提取循环记录中的字段(无需 %ROWTYPE,通过记录名.字段名访问) */
v_dept_id := dept_rec.department_id;
v_dept_name := dept_rec.department_name;
v_location := dept_rec.location;
/* 输出日志 */
test_log_info('场景1-部门信息:ID=' || v_dept_id || ', 名称=' || v_dept_name || ', 地点=' || v_location);
v_loop_count := v_loop_count + 1;
END LOOP;
test_log_info('场景1-单层循环执行完成,共遍历 ' || TO_CHAR(v_loop_count) || ' 个部门');
EXCEPTION
WHEN NO_DATA_FOUND THEN
test_log_info('场景1-无数据异常:未查询到任何部门信息');
WHEN OTHERS THEN
test_log_info('场景1-执行异常:错误代码=' || SQLCODE || ', 错误信息=' || SQLERRM);
END;
-- 转换后:
DECLARE
v_dept_id number(10);
v_dept_name varchar2(50);
v_location varchar2(50);
v_loop_count number := 0;
BEGIN
FOR dept_rec IN ((SELECT department_id,department_name,location FROM departments ORDER BY department_id)) LOOP
v_dept_id := dept_rec.department_id;
v_dept_name := dept_rec.department_name;
v_location := dept_rec.location;
test_log_info('场景1-部门信息:ID=' || v_dept_id || ', 名称=' || v_dept_name || ', 地点=' || v_location) ;
v_loop_count := v_loop_count+1;
END LOOP;
test_log_info('场景1-单层循环执行完成,共遍历 ' || TO_CHAR(v_loop_count) || ' 个部门') ;
EXCEPTION
WHEN no_data_found THEN
test_log_info('场景1-无数据异常:未查询到任何部门信息') ;
WHEN others THEN
test_log_info('场景1-执行异常:错误代码=' || SQLCODE || ', 错误信息=' || SQLERRM) ;
END;
1.3.2.11.6.6. 注释
单行注释和多行注释转换时,会被丢掉,不影响程序的执行。
语法
BEGIN
-- 单行注释
/*
多行注释
*/
END;
警告
转换后注释将会被丢弃、
仅支持单纯是注释的场景,不支持混入到一个语句中的场景,如下示例不支持:
EXECUTE IMMEDIATE v_sql
INTO v_emp_name, v_salary -- 接收查询结果
USING v_emp_id; -- 传入绑定变量值
示例
-- 转换前Oracle:
DECLARE
v date;
BEGIN
-- 这是单行注释
/*
这是多行注释
示例e.g SELECT CURRENT_DATE INTO v FROM dual
*/
SELECT CURRENT_DATE INTO v FROM dual /*这是测试*/ ;
END;
-- 转换后:
DECLARE
v date;
BEGIN
SELECT CURRENT_DATE INTO v FROM dual /*这是测试*/ ;
END;
1.3.2.11.6.7. 游标
语法
声明游标:
CURSOR cur_name IS select_statement;
打开:
OPEN cur_name;
获取游标:
FETCH cur_name INTO variable [,...];
关闭游标:
CLOSE cur_name;
警告
游标属性仅支持
NOTFOUNDFOUNDROWCOUNTISOPEN
示例
-- 转换前Oracle:
DECLARE
-- 1. 声明游标
CURSOR cur_employees IS
SELECT employee_id, name, salary
FROM employees
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;
-- 转换后:
DECLARE
CURSOR cur_employees IS SELECT employee_id,name,salary FROM employees 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;
1.3.2.11.6.8. 静态SQL
语法
SELECT ... ;
INSERT ... ;
DELETE ... ;
UPDATE ... ;
COMMIT;
示例
-- 转换前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;
COMMIT;
END;
-- 转换后:
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;
COMMIT;
END;
1.3.2.11.6.9. 动态SQL
语法
EXECUTE IMMEDIATE dynamic_sql_string
[INTO variable [,....]]
[USING bind_arg [,...]]
警告
当且仅当
dynamic_sql_string为字符串文本时,会对dynamic_sql_string进行SQL语句转换,其他情况(如变量,表达式)只会透传;不支持多字节的Q语法包裹字符串;
不支持动态语句中为匿名块。
匿名块仅支持在EXECUTE IMMEDIATE语句中对同一个对象进行DDL和DML操作;
示例
-- 转换前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;
-- 转换后:
DECLARE
v_employee_id number := 1;
v_name varchar2(50) := 'Bob Brown';
v_salary number := 55000;
v_department_id number := 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;
1.3.2.11.6.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;
警告
支持的预定义异常名称
NO_DATA_FOUNDZERO_DIVIDETOO_MANY_ROWSDUP_VAL_ON_INDEXINVALID_CURSOR;SQLCODESQLERRM内容和Oracle不保证一致 ;
示例
-- 转换前Oracle:
DECLARE
v_int NUMBER;
BEGIN
BEGIN
SELECT id into v_int from log_info_12116 where id = 2;
EXCEPTION
WHEN NO_DATA_FOUND THEN
test_log_info('内置异常:无数据匹配(NO_DATA_FOUND)');
WHEN OTHERS THEN
test_log_info('内置异常:无数据匹配(OTHERS)');
END;
EXCEPTION
WHEN OTHERS THEN
test_log_info('内置异常:类型转换失败(VALUE_ERROR)');
END;
-- 转换后
DECLARE
v_int number;
BEGIN
BEGIN
SELECT id into v_int from log_info_12116 where id = 2;
EXCEPTION
WHEN no_data_found THEN
test_log_info('内置异常:无数据匹配(NO_DATA_FOUND)') ;
WHEN others THEN
test_log_info('内置异常:无数据匹配(OTHERS)') ;
END;
EXCEPTION
WHEN others THEN
test_log_info('内置异常:类型转换失败(VALUE_ERROR)') ;
END;