注册
DM8 DMSQL 程序控制结构指南:从条件判断到循环跳转
专栏/技术分享/ 文章详情 /

DM8 DMSQL 程序控制结构指南:从条件判断到循环跳转

秧秧 2026/09/04 103 0 0
摘要

DM8 提供了过程化 SQL 编程能力,通常称为 DMSQL。其语法与 PL/SQL 高度兼容,并支持通过存储过程、存储函数、包等方式实现数据库端业务逻辑。

一、DMSQL程序是什么?为什么需要控制结构?

DMSQL程序是对标准SQL语言的扩展,是一种过程化SQL语言。普通的SQL语句只能执行单一的数据查询或操作,而DMSQL程序在此基础上增加了编程语言的特点——通过逻辑判断、循环控制等结构,可以实现复杂的功能和业务逻辑。
简单来说:SQL让你“问”数据库要数据,DMSQL程序让你“告诉”数据库怎么做。

DMSQL程序具备以下核心优势:

  • 更好的性能:存储模块在创建时会被编译成伪码序列,运行时无需重新进行编译和优化处理,同时可以减少应用程序与数据库之间的交互次数,从而提高执行效率。
  • 便于维护:存储模块集中存放在数据库中,业务逻辑与应用程序相对解耦。修改或重建存储模块后,调用它的应用程序通常无需修改或仅需少量调整。
  • 安全性高:存储模块可以封装具体的数据操作逻辑,将用户与底层数据对象进行隔离,并通过权限控制仅授予用户执行存储模块的权限,实现对数据访问范围的精细控制

DMSQL程序的控制结构,正是赋予SQL“智能”的关键所在。

二、DMSQL程序语句块结构:一切的基础

在深入控制结构之前,先了解DMSQL的基本组成单位——语句块(Block)。
一个完整的语句块由四部分组成:

DECLARE -- 声明部分:定义变量、常量、类型、游标等(可选) v_name VARCHAR2(50); v_age INT := 25; BEGIN -- 执行部分:编写各种PL/SQL语句和业务逻辑(必需) v_name := '张三'; DBMS_OUTPUT.PUT_LINE('姓名:' || v_name || ',年龄:' || v_age); EXCEPTION -- 异常处理部分:处理运行时错误(可选) WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('发生异常!'); END; --标记程序体部分结束

块可以嵌套——外层块中可以包含内层子块,类似于SQL中的子查询。

块的两大分类:

  • 命名块:存储过程、函数、触发器、包等,可存储在数据库中重复调用
  • 匿名块:没有名字,执行后即释放,不能再次引用

目前达梦数据库支持DMSQL程序的控制结构主要有三种:选择结构、循环结构以及跳转结构。

三、选择结构:让程序“做决策”

3.1 IF语句

IF语句通过布尔条件(TRUE / FALSE / NULL)来控制执行流程。
基本语法:

IF condition1 THEN statements1; [ELSIF condition2 THEN statements2;] [ELSE else_statements;] END IF;

示例:根据部门调整工资
假设输入一个员工号,根据所属部门决定工资涨幅:

DECLARE v_empno NUMBER(4) := 10; v_deptno NUMBER(5); v_increment NUMBER(4); BEGIN SELECT deptno INTO v_deptno FROM emp WHERE empno = v_empno; IF v_deptno = 10 THEN v_increment := 100; -- 10号部门涨100 ELSIF v_deptno = 20 THEN v_increment := 150; -- 20号部门涨150 ELSE v_increment := 200; -- 其他部门涨200 END IF; UPDATE emp SET sal = sal + v_increment WHERE empno = v_empno; END;

3.2 CASE语句

CASE语句提供两种形式:
(1)简单CASE语句:将一个表达式与多个值进行比较

CASE test_value WHEN value1 THEN statements1; WHEN value2 THEN statements2; ... [ELSE else_statements;] END CASE;

示例:根据成绩等级输出评语

DECLARE v_grade CHAR(1) := 'B'; v_result VARCHAR2(20); BEGIN v_result := CASE v_grade WHEN 'A' THEN 'Excellent' WHEN 'B' THEN 'Very Good' WHEN 'C' THEN 'Good' ELSE 'INPUT ERROR' END; DBMS_OUTPUT.PUT_LINE(v_result); END;

(2)搜索CASE语句:基于多个布尔条件进行判断

CASE WHEN condition1 THEN statements1; WHEN condition2 THEN statements2; ... [ELSE else_statements;] END CASE;

示例:根据工资区间决定涨幅

DECLARE v_empno NUMBER(4) := 10; v_sal NUMBER(10); v_increment NUMBER(4); BEGIN SELECT sal INTO v_sal FROM emp WHERE empno = v_empno; CASE WHEN v_sal < 1000 THEN v_increment := 200; -- 低于1000涨200 WHEN v_sal < 2000 THEN v_increment := 150; -- 1000-2000涨150 ELSE v_increment := 50; -- 2000以上涨50 END CASE; UPDATE emp SET sal = sal + v_increment WHERE empno = v_empno; END;

四、循环结构:让程序“重复做事”

循环结构用于重复执行一段代码,循环结构主要用到的有LOOP基本循环、WHILE循环、FOR循环语句。

4.1 LOOP基本循环

最简单的循环形式,需在循环体内包含EXIT语句退出,否则将陷入死循环
基本语法:

LOOP sequence_of_statement; EXIT [WHEN condition]; END LOOP;

示例:插入50条测试数据

DECLARE v_counter INTEGER; BEGIN v_counter := 1; LOOP INSERT INTO temp_table VALUES (v_counter, 'Loop index'); v_counter := v_counter + 1; EXIT WHEN v_counter > 50; END LOOP; END;

4.2 WHILE循环

当条件为TRUE时重复执行循环体,条件判断在循环开始前进行。
基本语法:

WHILE condition LOOP sequence_of_statement; END LOOP;

示例:使用WHILE插入50条数据

DECLARE
    v_counter INTEGER;
BEGIN
    v_counter := 1;
    WHILE v_counter <= 50 LOOP
        INSERT INTO temp_table VALUES (v_counter, 'Loop index');
        v_counter := v_counter + 1;
    END LOOP;
END;

4.3 FOR循环

基本语法

FOR loop_counter IN [REVERSE] low_bound .. high_bound LOOP sequence_of_statement; END LOOP;

系统默认时,循环变量从下界到上界递增。使用REVERSE关键字,循环变量从上界到下界递减。循环变量只能在循环体内使用,循环体外不可引用。

示例:FOR循环插入50条数据

BEGIN FOR v_counter IN 1 .. 50 LOOP INSERT INTO temp_table VALUES (v_counter, 'Loop index'); END LOOP; END;

示例:倒序循环

BEGIN FOR i IN REVERSE 1 .. 5 LOOP DBMS_OUTPUT.PUT_LINE('i = ' || i); END LOOP; END; -- 输出:i = 5, i = 4, i = 3, i = 2, i = 1

实战建议:批量数据处理是循环的典型场景。例如,使用FOR循环向表中批量插入大量数据,或用循环遍历游标结果集逐行处理。

五、跳转结构:GOTO语句

GOTO语句实现无条件跳转,将控制权转移到指定标号处。
基本语法:

<<label_name>> ... GOTO label_name;

使用限制(这也是不建议滥用GOTO的原因):

  1. 不能从循环体外跳入循环体内
  2. 不能跳入IF语句内部
  3. 不能从子程序外部跳到子程序内部
  4. 外层块可跳转到内层块,但内层块不能跳转到外层块
DECLARE v_counter INTEGER := 1; BEGIN <<loop_start>> DBMS_OUTPUT.PUT_LINE('Counter: ' || v_counter); v_counter := v_counter + 1; IF v_counter <= 5 THEN GOTO loop_start; -- 跳回标号处 END IF; END;

提醒:大多数场景下,使用IF、CASE和循环结构完全可以替代GOTO的功能。

六、使用建议

  • 选择合适的循环:知道循环次数用FOR,条件控制用WHILE,需要在循环体中间判断退出用LOOP
  • 避免死循环:使用LOOP时务必包含EXIT语句
  • 谨慎使用GOTO:能用IF/CASE/循环解决的问题,就不要用GOTO
  • 善用EXIT和CONTINUE:让循环逻辑更清晰、代码更简洁
评论
后发表回复

作者

文章

阅读量

获赞

扫一扫
联系客服