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. 数据类型

基础类型转换 参见 数据类型转换映射表

警告

  1. BINARY_FLOAT, BINARY_DOUBLE 源库和目标库精度存在差异,以实际目标库为准。如1.3 在Oracle中输出为1.29999995E+000(不同环境可能存在差异),而在DM中输出为1.3E0;

  2. DM匿名块不支持 long raw rowid urowid xmltype 类型;

  3. DM匿名块 nchar(n), nvarchar2(n), n表示的是字节数,不是字符数, 和Oracle不一致;

  4. 定义的 integer 类型变量,会转换成 int 类型;

1.3.2.11.6.3. 声明

语法

variable_name [CONSTANT] datatype [NOT NULL] [:=|DEFAULT] expression

警告

  1. 不支持出现重名的变量;

  2. 变量名不能是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;

警告

  1. 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;

警告

  1. 转换后注释将会被丢弃、

  2. 仅支持单纯是注释的场景,不支持混入到一个语句中的场景,如下示例不支持:

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;

警告

  1. 游标属性仅支持 NOTFOUND FOUND ROWCOUNT ISOPEN

示例

-- 转换前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 [,...]]

警告

  1. 当且仅当 dynamic_sql_string 为字符串文本时,会对 dynamic_sql_string 进行SQL语句转换,其他情况(如变量,表达式)只会透传;

  2. 不支持多字节的Q语法包裹字符串;

  3. 不支持动态语句中为匿名块。

  4. 匿名块仅支持在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;

警告

  1. 支持的预定义异常名称 NO_DATA_FOUND ZERO_DIVIDE TOO_MANY_ROWS DUP_VAL_ON_INDEX INVALID_CURSOR ;

  2. SQLCODE SQLERRM 内容和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;