经济文库 - 千万精品文档,你想要的都能搜到,下载即用。

KingbaseES_PLSQL过程语言参考手册.pdf

_Thorns617 页 3.217 MB 访问 672.97下载文档
KingbaseES_PLSQL过程语言参考手册.pdfKingbaseES_PLSQL过程语言参考手册.pdfKingbaseES_PLSQL过程语言参考手册.pdfKingbaseES_PLSQL过程语言参考手册.pdfKingbaseES_PLSQL过程语言参考手册.pdfKingbaseES_PLSQL过程语言参考手册.pdf
当前文档共617页 2.97
下载后继续阅读

KingbaseES_PLSQL过程语言参考手册.pdf

KingbaseES PL/SQL 过程语言参考手册 金仓数据库管理系统 KingbaseES 文档版本:V9(V009R001C001B0024) 发布日期:2023 年 10 月 12 日 北京人大金仓信息技术股份有限公司 目 目 录 录 第 1 章 PL/SQL 前言 1 1.1 读者 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.2 关联文档 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.3 手册约定 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 第 2 章 PL/SQL 概述 2.1 2.2 PL/SQL 的优势 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 2.1.1 与 SQL 的紧密集成 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 2.1.2 高性能 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 2.1.3 高生产力 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 2.1.4 可移植性 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 2.1.5 可扩展性 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 2.1.6 可管理性 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 2.1.7 支持面向对象编程 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 PL/SQL 的主要特点 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 2.2.1 错误处理 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 2.2.2 块 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 2.2.3 变量和常量 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 2.2.4 子程序 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 2.2.5 包 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 2.2.6 触发器 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 2.2.7 输入和输出 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 2.2.8 数据抽象 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 2.2.8.1 游标 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 2.2.8.2 复合变量 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 2.2.8.3 使用%ROWTYPE 属性 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 2.2.8.4 使用%TYPE 属性 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 2.2.8.5 抽象数据类型 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 控制语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 2.2.10 一次处理查询结果集的一行 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 PL/SQL 的架构 10 2.2.9 2.3 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I 目 录 2.3.1 PL/SQL 引擎 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 2.3.2 PL/SQL 单元和编译参数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 第 3 章 PL/SQL 语言基础 3.1 3.2 12 字符集 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 3.1.1 数据库字符集 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 3.1.2 国际字符集 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 3.1.3 关于字符排序 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 词法单元 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 3.2.1 分隔符 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 3.2.2 标识符 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 3.2.2.1 保留关键字和非保留关键字 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 3.2.2.2 用户自定义标识符 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 3.2.2.2.1 普通自定义用户标识符 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 3.2.2.2.2 带引号的用户自定义标识符 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 3.2.3 常数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 3.2.4 编译指令 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 3.2.5 注释 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 3.2.5.1 单行注释 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 3.2.5.2 多行注释 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 词法单元之间的空白字符 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 声明 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 3.3.1 NOT NULL 约束 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 3.3.2 声明变量 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 3.3.3 声明常量 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 3.3.4 常量和变量的初始化 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 3.3.5 使用%TYPE 属性声明 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 3.4 对标识符的引用 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 3.5 标识符的作用域和可见性 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 3.6 为变量赋值 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 3.6.1 用赋值语句为变量赋值 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 3.6.2 用 SELECT INTO 语句为变量赋值 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 3.6.3 将值作为子程序的参数分配给变量 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 3.6.4 给 BOOLEAN 变量赋值 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 表达式 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 3.7.1 连接运算符 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 3.7.2 运算符优先级 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 3.7.3 逻辑运算符 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 3.7.4 短路计算 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 3.7.5 比较运算符 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 3.7.5.1 IS [NOT] NULL 运算符 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 3.7.5.2 关系运算符 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 3.2.6 3.3 3.7 II 目 录 3.7.5.2.1 算数比较 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 3.7.5.2.2 布尔比较 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 3.7.5.2.3 字符比较 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 3.7.5.2.4 时间比较 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 3.7.5.3 LIKE 运算符 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 3.7.5.4 BETWEEN 运算符 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 3.7.5.5 IN 运算符 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 3.7.6 布尔表达式 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 3.7.7 CASE 表达式 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 3.7.7.1 简单的 CASE 表达式 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 3.7.7.2 搜索 CASE 表达式 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 静态表达式 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 3.7.8.1 PLS_INTEGER 静态表达式 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 3.7.8.2 BOOLEAN 静态表达式 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 3.7.8.3 VARCHAR2 静态表达式 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 3.7.8.4 静态常量 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 错误报告函数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 3.7.8 3.8 第 4 章 数据类型 58 SQL 数据类型 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 4.1.1 BINARY_FLOAT 和 BINARY_DOUBLE 的其他 PL/SQL 子类型 . . . . . . . . . . . . . . . 58 4.1.2 CHAR 和 VARCHAR2 变量 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 4.1.2.1 分配或插入过长的值 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 4.1.2.2 为多字节字符声明变量 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 4.1.2.3 CHAR 和 VARCHAR2 数据类型的区别 . . . . . . . . . . . . . . . . . . . . . . . . . . 60 4.1.2.3.1 预定义子类型 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 4.1.2.3.2 空白填充的工作原理 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 4.1.2.3.3 值比较 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 4.2 布尔数据类型 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 4.3 PLS_INTEGER 和 BINARY_INTEGER 数据类型 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 4.3.1 防止 PLS_INTEGER 溢出 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 4.3.2 预定义的 PLS_INTEGER 子类型 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 4.3.3 PLS_INTEGER 的 SIMPLE_INTEGER 子类型 . . . . . . . . . . . . . . . . . . . . . . . . . . 66 用户定义的 PL/SQL 子类型 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 4.4.1 无约束子类型 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 4.4.2 约束子类型 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 4.4.3 具有相同数据类型簇的基类型的子类型 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 4.1 4.4 第 5 章 控制语句 5.1 70 条件选择语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 5.1.1 IF THEN 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 5.1.2 IF THEN ELSE 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 5.1.3 IF THEN ELSIF 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 III 5.2 5.3 目 录 5.1.4 简单 CASE 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 5.1.5 搜索 CASE 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 循环语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 5.2.1 基本循环语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 5.2.2 FOR LOOP 语句概述 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 5.2.2.1 FOR LOOP 迭代 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80 5.2.2.2 步进范围迭代控制 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82 5.2.2.3 游标迭代控制 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84 5.2.2.4 在迭代控制中使用动态 SQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 5.2.3 WHILE LOOP 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86 5.2.4 FOREACH 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86 顺序控制语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 5.3.1 GOTO 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 5.3.2 NULL 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 第 6 章 PL/SQL 集合和记录 90 6.1 集合类型 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 6.2 关联数组 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91 6.2.1 声明关联数组常量 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 6.2.2 关联数组的适用情况 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 6.3 可变数组 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 可变数组的适用情况 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 嵌套表 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 6.4.1 嵌套表和可变数组的区别 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99 6.4.2 嵌套表适用情况 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100 6.3.1 6.4 6.5 集合的构造函数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100 6.6 集合变量赋值 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101 6.6.1 数据类型兼容 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101 6.6.2 给可变数组和嵌套表变量赋 NULL 值 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102 6.7 多维集合 6.8 集合的比较 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105 6.9 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103 6.8.1 将可变数组和嵌套表变量与 NULL 进行比较 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105 6.8.2 嵌套表的相等和不相等比较 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106 集合方法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107 6.9.1 DELETE 方法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107 6.9.2 TRIM 方法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111 6.9.3 EXTEND 方法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113 6.9.4 EXISTS 方法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114 6.9.5 FIRST 和 LAST 方法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115 6.9.5.1 用于关联数组的 FIRST 和 LAST 方法 . . . . . . . . . . . . . . . . . . . . . . . . . . 115 6.9.5.2 用于 Varray 数组的 FIRST 和 LAST 方法 . . . . . . . . . . . . . . . . . . . . . . . . 117 6.9.5.3 用于嵌套表的 FIRST 和 LAST 方法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118 IV 目 6.9.6 录 COUNT 方法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120 6.9.6.1 用于 Varray 数组的 COUNT 方法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120 6.9.6.2 用于嵌套表的 COUNT 方法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121 6.9.7 LIMIT 方法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122 6.9.8 PRIOR 和 NEXT 方法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123 6.10 包规范中定义的集合类型 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126 6.11 记录类型变量 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127 6.11.1 记录类型变量的初始化值 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128 6.11.2 声明 record 常量 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128 6.11.3 RECORD 类型 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128 6.11.4 使用%ROWTYPE 属性声明 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 6.11.4.1 声明始终代表整行的 record 变量 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 6.11.4.2 声明表示部分行的 record 变量 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133 6.11.4.3 %ROWTYPE 属性和虚拟列 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135 6.11.4.4 %ROWTYPE 属性和隐藏列 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136 6.12 给 record 变量赋值 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137 6.12.1 record 变量相互赋值 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137 6.12.2 将整行或部分行赋值给 record 变量 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140 6.12.2.1 使用 SELECT INTO 将一行赋值给 record 变量 . . . . . . . . . . . . . . . . . . . . . 140 6.12.2.2 使用 FETCH 将一行赋值给 record 变量 . . . . . . . . . . . . . . . . . . . . . . . . . . 141 6.12.2.3 使用 SQL 语句将行返回到 PL/SQL record 变量 . . . . . . . . . . . . . . . . . . . . . 142 6.12.3 NULL 赋值给 record 变量 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143 6.13 RECORD 类型的比较 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143 6.14 向表中插入 record . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144 6.15 通过 Record 变量后更新行 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145 6.16 对 record 变量插入和更新的限制 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146 第 7 章 PL/SQL 静态 SQL 7.1 静态 SQL 说明 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147 7.1.1 声明 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147 7.1.2 伪列 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149 7.1.2.1 7.2 147 游标概述 7.2.1 7.2.2 PL/SQL 中的 CURRVAL 和 NEXTVAL . . . . . . . . . . . . . . . . . . . . . . . . . 149 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151 隐式游标 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151 7.2.1.1 SQL%ISOPEN 属性:游标是否打开? 7.2.1.2 SQL%FOUND 属性:是否有任何行受到影响? . . . . . . . . . . . . . . . . . . . . . . 152 7.2.1.3 SQL%NOTFOUND 属性:没有行受到影响? 7.2.1.4 SQL%ROWCOUNT 属性:有多少行受到影响? . . . . . . . . . . . . . . . . . . . . . 154 . . . . . . . . . . . . . . . . . . . . . . . . . . 152 . . . . . . . . . . . . . . . . . . . . . . 153 显式游标 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155 7.2.2.1 声明和定义显式游标 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156 7.2.2.2 打开和关闭显式游标 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157 7.2.2.3 使用显式游标获取数据 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157 V 目 7.2.2.4 显式游标查询中的变量 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160 7.2.2.5 当显式游标查询需要列别名时 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163 7.2.2.6 显式游标的参数处理 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164 7.2.2.7 7.3 7.5 7.6 7.2.2.6.1 具有默认值的形参游标参数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165 7.2.2.6.2 添加带默认值的形参游标参数 . . . . . . . . . . . . . . . . . . . . . . . . . . . 167 显式游标属性 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168 7.2.2.7.1 %ISOPEN 属性:游标是否打开? . . . . . . . . . . . . . . . . . . . . . . . . 168 7.2.2.7.2 %FOUND 属性:游标是否读取到数据?? . . . . . . . . . . . . . . . . . . . 169 7.2.2.7.3 %NOTFOUND 属性:游标是否未读取到数据?? . . . . . . . . . . . . . . . 170 7.2.2.7.4 %ROWCOUNT 属性:游标读取到多少行数据? . . . . . . . . . . . . . . . . 171 处理查询结果集 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172 7.3.1 7.4 录 使用 SELECT INTO 语句处理查询结果集 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172 7.3.1.1 处理单行结果集 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172 7.3.1.2 处理大型多行结果集 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172 7.3.2 使用游标 FOR LOOP 语句处理查询结果集 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172 7.3.3 使用显式游标、OPEN、FETCH 和 CLOSE 处理查询结果集 . . . . . . . . . . . . . . . . . . . 175 7.3.4 处理带有子查询的查询结果集 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175 游标变量 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177 7.4.1 创建游标变量 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177 7.4.2 打开和关闭游标变量 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179 7.4.3 使用游标变量获取数据 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179 7.4.4 为游标变量赋值 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181 7.4.5 游标变量查询中的变量 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182 7.4.6 查询集合 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184 7.4.7 游标变量属性 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185 7.4.8 游标变量作为子程序参数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185 事务处理和控制 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188 7.5.1 COMMIT 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188 7.5.2 ROLLBACK 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189 7.5.3 隐式回滚 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191 7.5.4 SET TRANSACTION 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191 7.5.5 覆盖默认锁定 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192 7.5.5.1 LOCK TABLE 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192 7.5.5.2 SELECT FOR UPDATE 和 FOR UPDATE 游标 . . . . . . . . . . . . . . . . . . . . 192 7.5.5.3 用 ROWID 伪列模拟 CURRENT OF Clause . . . . . . . . . . . . . . . . . . . . . . . 193 自治事务 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195 7.6.1 自治事务的优势 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195 7.6.2 事务上下文 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195 7.6.3 交易可见性 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195 7.6.4 声明自治例程 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196 7.6.5 控制自治事务 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197 7.6.6 进入和退出自治程序 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198 VI 目 录 7.6.7 提交和回滚自治事务 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198 7.6.8 从 SQL 调用自治函数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198 第 8 章 PL/SQL 动态 SQL 200 8.1 何时需要动态 SQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200 8.2 Native dynamic SQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201 8.3 8.4 8.2.1 EXECUTE IMMEDIATE 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201 8.2.2 OPEN FOR、FETCH 和 CLOSE 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207 8.2.3 动态 SQL 语句中的重复占位符名称 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208 8.2.3.1 动态 SQL 语句不是匿名块或 CALL 语句 . . . . . . . . . . . . . . . . . . . . . . . . . 208 8.2.3.2 动态 SQL 语句是匿名块或 CALL 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . 209 DBMS_SQL 包 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210 8.3.1 DBMS_SQL.RETURN_RESULT 过程 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210 8.3.2 DBMS_SQL.GET_NEXT_RESULT 过程 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212 8.3.3 DBMS_SQL.TO_REFCURSOR 函数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214 8.3.4 DBMS_SQL.TO_CURSOR_NUMBER 函数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215 SQL 注入 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216 8.4.1 8.4.2 SQL 注入技术 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216 8.4.1.1 语句修改 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216 8.4.1.2 语句注入 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217 防范 SQL 注入 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220 8.4.2.1 第 9 章 PL/SQL 子程序 绑定变量 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220 222 9.1 使用子程序的原因 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222 9.2 嵌套、包和独立子程序 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223 9.3 子程序调用 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223 9.4 子程序属性 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223 9.5 子程序的构成 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224 9.5.1 函数的额外部分 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225 9.5.2 RETURN 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226 9.5.2.1 函数中的 RETURN 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226 9.5.2.2 过程中的 RETURN 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228 9.5.2.3 匿名块中的 RETURN 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229 9.6 子程序声明 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229 9.7 子程序参数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230 9.7.1 子程序参数的形参和实参 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230 9.7.1.1 受约束子类型的形式化参数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232 9.7.2 子程序参数传递方法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233 9.7.3 子程序参数模式 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234 9.7.4 子程序参数别名 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241 9.7.4.1 9.7.5 游标变量参数的子程序参数别名 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241 IN 子程序参数的默认值 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242 VII 目 9.7.6 录 实际参数的位置、命名和混合表示法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245 9.8 子程序名称解析 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247 9.9 子程序的重载 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249 9.9.1 不能被重载的子程序 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250 9.9.2 子程序重载错误 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250 9.10 子程序的递归调用 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252 9.11 修改子程序可能引入不确定问题 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254 9.12 SQL 语句可以调用的 PL/SQL 函数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255 9.13 调用者的权限和定义者的权限(AUTHID 属性) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256 9.13.1 为 PL/SQL 包和独立子程序授予角色 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256 9.13.2 IR 单元需要模板对象 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257 第 10 章 触发器 258 10.1 触发器概述 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258 10.2 使用触发器的理由 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259 10.3 DML 触发器 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260 10.3.1 检测触发 DML 语句的条件谓词 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260 10.3.2 INSTEAD OF DML 触发器 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261 10.3.3 触发器在完整性检查的应用 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263 10.3.3.1 子表的外键触发 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264 10.3.3.2 父表的 UPDATE 和 DELETE RESTRICT 触发器 . . . . . . . . . . . . . . . . . . . . 265 10.3.3.3 父表的 UPDATE 和 DELETE SET NULL 触发器 . . . . . . . . . . . . . . . . . . . . 266 10.3.3.4 父表的删除级联触发器 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266 10.3.3.5 父表的更新级联触发器 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266 10.3.3.6 复杂约束检查的触发器 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268 10.3.3.7 复杂安全授权的触发器 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269 10.3.4 导出列值的触发器 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270 10.4 相关名称和伪记录 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271 10.5 事件触发器 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275 10.6 触发器调用的子程序 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275 10.7 触发编译、失效和重新编译 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276 10.8 触发器中的异常处理 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276 10.9 触发器设计指南 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277 10.10 触发器触发的顺序 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278 10.11 触发器启用和禁用 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278 10.12 触发器更改和调试 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279 10.13 触发器信息视图 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279 第 11 章 包 11.1 包的优点 281 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281 11.2 包规范 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282 11.2.1 包中公共项 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282 11.2.2 创建包规范 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283 VIII 目 录 11.3 包体 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285 11.4 包实例化和初始化 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286 11.5 包编写指南 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286 11.6 包示例 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287 11.7 关于 STANDARD 包 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291 11.8 DBMS_RANDOM 包 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291 11.8.1 DBMS_RANDOM 包下函数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291 第 12 章 异常处理 296 12.1 异常处理概述 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296 12.1.1 异常种类 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297 12.1.2 异常处理程序的优点 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297 12.1.3 异常处理指南 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300 12.2 系统预定义异常 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301 12.3 用户自定义异常 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304 12.4 重新声明预定义的异常 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304 12.5 显式触发异常 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306 12.5.1 RAISE 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306 12.5.1.1 使用 RAISE 语句触发用户自定义的异常 . . . . . . . . . . . . . . . . . . . . . . . . . 307 12.5.1.2 使用 RAISE 语句触发系统预定义的异常 . . . . . . . . . . . . . . . . . . . . . . . . . 308 12.5.1.3 使用 RAISE 语句重新触发当前异常 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 309 12.5.2 存储过程 RAISE_APPLICATION_ERROR . . . . . . . . . . . . . . . . . . . . . . . . . . . . 310 12.6 异常传播 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 311 12.6.1 未传播的异常 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 312 12.6.2 从内部块传播到外部块的异常 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313 12.6.3 声明中触发的异常的传播 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314 12.6.4 异常处理程序中抛出的异常传播 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315 12.7 未处理的异常 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320 12.8 检索异常信息 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321 12.8.1 SQLSTATE 变量 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321 12.8.2 SQLERRM 变量 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321 12.8.3 SQLCODE 函数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322 12.8.4 SQLERRM 函数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322 12.8.5 ERROR_LINE 函数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322 12.8.6 ERROR_PROCEDURE 函数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322 12.8.7 ERROR_NUMBER 函数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323 12.8.8 ERROR_MESSAGE 函数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323 12.8.9 ERROR_STATE 函数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323 12.8.10 ERROR_SEVERITY 函数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323 12.8.11 SYS_GET_PREDEFINED_EXCEPTION_DETAIL 函数 . . . . . . . . . . . . . . . . . . . . 324 12.9 异常捕获 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325 12.10 获取异常状态信息 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328 IX 目 录 12.11 获取执行位置信息 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 329 12.12 检查断言 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 330 第 13 章 PL/SQL 优化和调优 332 13.1 最小化 CPU 开销 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332 13.1.1 调优 SQL 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332 13.1.2 优化在查询中的函数调用 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332 13.1.3 调优循环语句中的 SQL 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334 13.1.4 调优计算密集型 PL/SQL 代码 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334 13.1.4.1 在性能关键代码中避免约束子类型 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334 13.1.4.2 最小化隐式数据类型转换 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334 13.1.5 使用 SQL 字符函数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 335 13.1.6 将最简单的条件测试放在首位 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 335 13.2 批量 SQL 和批量绑定 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336 13.2.1 FORALL 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336 13.2.1.1 对稀疏集合使用 FORALL 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340 13.2.1.2 FORALL 语句中未处理的异常 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343 13.2.1.3 立即处理 FORALL 异常 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344 13.2.1.4 FORALL 语句完成后处理 FORALL 异常 . . . . . . . . . . . . . . . . . . . . . . . . . 346 13.2.1.4.1 稀疏集合和 SQL%BULK_EXCEPTIONS . . . . . . . . . . . . . . . . . . . . 349 13.2.1.4.2 获取受 FORALL 语句影响的行数 . . . . . . . . . . . . . . . . . . . . . . . . 349 13.2.2 BULK COLLECT 子句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351 13.2.2.1 带有 BULK COLLECT 子句的 SELECT INTO 语句 . . . . . . . . . . . . . . . . . . 351 13.2.2.1.1 SELECT BULK COLLECT INTO 语句和别名 . . . . . . . . . . . . . . . . . 354 13.2.2.1.1.1 SELECT BULK COLLECT INTO 语句的行限制 . . . . . . . . . . . . 359 13.2.2.1.1.2 循环编译集合的规则 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360 13.2.2.2 带有 BULK COLLECT 子句的 FETCH 语句 . . . . . . . . . . . . . . . . . . . . . . . 360 13.2.2.2.1 FETCH BULK COLLECT 语句的行限制 . . . . . . . . . . . . . . . . . . . . 364 13.2.2.3 RETURNING INTO 子句带有 BULK COLLECT 子句 . . . . . . . . . . . . . . . . . 365 13.2.3 FORALL 语句和 BULK COLLECT 子句一起使用 . . . . . . . . . . . . . . . . . . . . . . . . . 366 13.3 Pipelined Table 函数的多重转换 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368 13.3.1 Table 函数概述 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368 13.3.2 创建 Pipelined Table 函数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368 13.3.3 Pipelined Table 函数作为转换函数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 370 13.4 分析和跟踪 PL/SQL 程序 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 372 第 14 章 语言元素 373 14.1 赋值语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373 14.2 AUTONOMOUS_TRANSACTION 编译指示 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 375 14.3 基本 LOOP 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 376 14.4 块 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379 14.5 CASE 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391 14.6 CLOSE 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 393 X 目 录 14.7 集合方法调用 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394 14.8 集合变量声明 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 397 14.9 注释 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 402 14.10 常量声明 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 403 14.11 CONTINUE 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 404 14.12 游标 FOR LOOP 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 407 14.13 游标变量声明 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 408 14.14 数据类型属性 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 411 14.15 DELETE 语句扩展 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413 14.16 DETERMINISTIC 子句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413 14.17 元素规范 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413 14.18 EXCEPTION_INIT 编译指示 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 417 14.19 异常声明 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 418 14.20 异常处理程序 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 419 14.21 EXECUTE IMMEDIATE 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 421 14.22 EXIT 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 424 14.23 显式游标声明和定义 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 426 14.24 表达式 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 430 14.25 FETCH 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 442 14.26 FOR LOOP 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 444 14.27 FORALL 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 447 14.28 FOREACH 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 449 14.29 形式参数声明 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 451 14.30 函数声明与定义 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 453 14.31 GOTO 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 455 14.32 GET 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 459 14.33 IF 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 460 14.34 隐式游标属性 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 461 14.35 调用者权限和定义者权限属性 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 464 14.36 INSERT 语句扩展 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 465 14.37 迭代器 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 466 14.38 命名游标属性 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 472 14.39 NULL 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 475 14.40 OPEN 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 475 14.41 OPEN FOR 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 476 14.42 PERFORM 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 478 14.43 PIPE ROW 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 479 14.44 PIPELINED 子句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 480 14.45 过程声明与定义 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 481 14.46 RAISE 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 483 14.47 记录变量声明 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 489 14.48 RETURN 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 491 XI 目 录 14.49 RETURN NEXT 以及 RETURN QUERY 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 493 14.50 RETURNING INTO 子句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 495 14.51 RESULT_CACHE 子句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 498 14.52 %ROWTYPE 属性 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 498 14.53 标量变量声明 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 500 14.54 SELECT INTO 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 501 14.55 SQLCODE 函数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 502 14.56 SQLERRM 函数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 503 14.57 %TYPE 属性 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 505 14.58 UPDATE 语句扩展 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 507 14.59 WHILE LOOP 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 508 第 15 章 用于存储 PL/SQL 单元的 SQL 语句 511 15.1 ALTER FUNCTION 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 512 15.2 ALTER PACKAGE 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 516 15.3 ALTER PROCEDURE 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 517 15.4 ALTER TRIGGER 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 520 15.5 ALTER TYPE 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 521 15.6 CREATE FUNCTION 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 525 15.7 CREATE PACKAGE 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 535 15.8 CREATE PACKAGE BODY 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 539 15.9 CREATE PROCEDURE 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 542 15.10 CREATE TRIGGER 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 548 15.11 CREATE TYPE 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 557 15.12 CREATE TYPE BODY 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 562 15.13 DROP FUNCTION 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 568 15.14 DROP PACKAGE 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 569 15.15 DROP PACKAGE BODY 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 570 15.16 DROP PROCEDURE 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 571 15.17 DROP TRIGGER 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 572 15.18 DROP TYPE 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 573 15.19 DROP TYPE BODY 语句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 574 第 16 章 A PL/SQL 源文本加密 575 16.1 PL/SQL 源文本加密限制 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 575 16.2 PL/SQL 源文本加密指南 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 575 16.3 使用 PL/SQL Wrapper 工具加密 PL/SQL 源文本 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 576 16.4 用 DBMS_DDL 子程序加密 PL/SQL 源文本 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 580 第 17 章 B PL/SQL 名称解析 583 17.1 限定名称和点符号 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 583 17.2 列名优先级 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 585 17.3 PL/SQL 和 SQL 名称解析规则的区别 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 586 XII 目 录 17.4 静态 SQL 语句中的名称解析 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 587 第 18 章 C PL/SQL 程序限制 588 第 19 章 D PL/SQL 保留字和关键字 590 第 20 章 E PL/SQL 预定义类型 593 第 21 章 F PLSQL 对象支持状态 595 21.1 限制与约束 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 595 21.2 控制参数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 596 21.3 对象状态刷新 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 596 21.3.1 被动刷新 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 596 21.3.2 主动刷新 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 596 21.3.3 示例 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 597 21.4 状态蔓延 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 599 21.5 相关视图 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 600 版权声明 601 服务周期承诺 602 XIII 第 1 章 PL/SQL 前言 1 第 章 PL/SQL 前言 PL/SQL 过程语言参考手册描述并解释了如何使用 PL/SQL,即 PL/SQL 是 SQL 的过程语言扩展 1.1 读者 PL/SQL 过程语言参考手册适用于基于 PL/SQL 应用程序在 KingbaseES 上做开发的任何人,包括: • 程序员 • 系统分析师 • 项目经理 • 数据库管理员 要有效地使用本文档,需要具备以下方面的工作知识: • KingbaseES 数据库 • 结构化查询语言 (SQL) • 基本编程概念,如 IF-THEN 语句、循环、过程和函数 1.2 关联文档 有关详细信息,请参阅 KingbaseES 数据库文档库中的以下文档: • 《KingbaseES SQL 语言参考手册》 • 《KingbaseES 数据库开发指南》 • 《KingbaseES 数据库管理员指南》 • 《KingbaseES 数据库概念》 • 《KingbaseES 数据库性能调优指南》 1 第 1 章 PL/SQL 前言 1.3 手册约定 本文档中可能出现“注意、提示、警告、另请参阅”等标志,它们所代表的含义如下: 注意: 用于突出重要/关键信息、最佳实践等。 提示: 用于突出小窍门、捷径等。 警告: 用于传递设备或环境安全警示信息,若不避免,可能会导致设备损坏、数据丢失、设备性能降低或其 它不可预知的结果。 另请参阅: 用于突出参考、参阅等。 以下程序代码书写约定适用于本文档: 符号 说明 [] 表示包含一个或多个可选项。不需要输入中括号本身。 {} 表示包含两个以上(含两个)的候选,必须在其中选取一个。不需要输入花括号本身。 | 分割中括号或者花括号中的两个或两个以上选项。不需要输入“|”本身。 ... 表示其之前的元素可以被重复。 斜体 表示占位符或者需要提供特定值的变量。 大写 表示系统提供的元素,以便与用户定义的元素相互区分。除出现在方括号中的元素外,应当按 照顺序逐字输入。当然,部分元素在系统中是大小写不敏感的,因此用户可以根据系统说明以 小写形式输入。 小写 表示由用户提供的元素。 2 第 2 章 PL/SQL 概述 2 第 章 PL/SQL 概述 PL/SQL 语言是 SQL 语言在 KingbaseES 数据库上的过程扩展语言,是一种可移植的高性能事务处理语言。本 概述解释了它的优点并简要描述了它的主要特性和架构。 2.1 PL/SQL 的优势 PL/SQL 提供了优于其他编程语言的几个优点。PL/SQL 具有以下优点: • 与 SQL 的紧密集成 • 高性能 • 高生产力 • 可移植性 • 可扩展性 • 可管理性 • 支持面向对象编程 2.1.1 与 SQL 的紧密集成 PL/SQL 与使用最广泛的数据库操作语言 SQL 紧密集成。 例如: • PL/SQL 允许您使用所有 SQL 数据操作、游标控制和事务控制语句,以及所有 SQL 函数、运算符和伪列。 • PL/SQL 完全支持 SQL 数据类型。 您无需在 PL/SQL 和 SQL 数据类型之间进行转换。例如,如果您的 PL/SQL 程序从 SQL 类型 VARCHAR2 的列中检索一个值,它可以将该值存储在 VARCHAR2 类型的 PL/SQL 变量中。 您可以为 PL/ SQL 数据类型指定数据库表的列或行的数据类型,而无需显式指定该数据类型(请参阅使 用%TYPE 属性 和使用%ROWTYPE 属性 )。 3 第 2 章 PL/SQL 概述 • PL/SQL 允许您运行 SQL 查询并一次处理查询结果集的一行(请参阅一次处理查询结果集的一行 )。 • PL/SQL 函数可以在 SQL SELECT 语句的 WITH 子句中声明和定义(请参阅《KingbaseES SQL 语言参考手 册》)。 PL/SQL 支持静态和动态 SQL。静态 SQL 是其全文在编译时已知的 SQL。动态 SQL 是直到运行时才知道全文 的 SQL。动态 SQL 使您的应用程序更加灵活和通用。 另请参阅: PL/SQL 静态 SQL 和PL/SQL 动态 SQL 获取详细信息。 2.1.2 高性能 PL/SQL 允许您将语句块发送到数据库,从而显著的减少应用程序和数据库之间的流量。 • 绑定变量 当您在 PL/SQL 代码中直接嵌入 SQL 的 INSERT、UPDATE、DELETE、MERGE 或 SELECT 语句时,PL/ SQL 编译器会将 WHERE 和 VALUES 子句中的变量转换为绑定变量(有关详细信息,请参阅“静态 SQL 语 句中的名称解析 ”)。 KingbaseES 数据库可以在每次运行相同的代码时重用这些 SQL 语句,从而提高性能。 当您使用动态 SQL 时,PL/SQL 不会自动创建绑定变量,但是您可以通过显式指定它们来将它们与动态 SQL 一起使用(有关详细信息,请参阅“EXECUTE IMMEDIATE 语句 ”)。 • 子程序 PL/SQL 子程序以明文的形式存储。每个 session 在第一次调用子程序都需要进行一次编译,且保存编译结果。 需要注意的是,每个 Seesion 只缓存 128 个子程序的结果,超过 128 个子程序被调用,新调用的子程序的编译 结果会覆盖旧的子程序编译结果。 2.1.3 高生产力 PL/SQL 有许多节省设计和调试时间的特性,而且在所有环境中都是一样的。 PL/SQL 允许您编写紧凑的代码来操作数据。就像 PERL 这样的脚本语言可以读取、转换和写入文件中的数据 一样,PL/SQL 可以查询、转换和更新数据库中的数据。 如果您学会将 PL/SQL 与一种工具一起使用,则可以将您的知识转移到其他工具上。有关 PL/SQL 特性的概 述,请参阅“PL/SQL 的主要特点 ”。 2.1.4 可移植性 PL/SQL 是用于数据库开发的可移植标准语言。 您可以在运行数据库的任何操作系统和平台上运行 PL/SQL 应用程序。 4 第 2 章 PL/SQL 概述 2.1.5 可扩展性 PL/SQL 存储子程序通过将应用程序处理集中在数据库服务器上来提高可伸缩性。有关子程序的更多信息,请参 阅“子程序 ”。 2.1.6 可管理性 PL/SQL 存储的子程序提高了可管理性,因为您可以在数据库服务器上只维护一个子程序的副本,所有的客户端 的缓存结果都是通过这一个副本生成。 任何的应用程序都可以使用子程序,您可以更改子程序而不影响调用它们的应用程序。有关子程序的更多信息, 请参阅“子程序 ”。 2.1.7 支持面向对象编程 PL/SQL 允许定义可用于面向对象设计的对象类型。 PL/SQL 支持带有“抽象数据类型 ”的面向对象编程。 2.2 PL/SQL 的主要特点 PL/SQL 结合了 SQL 的数据操作能力和过程语言的处理能力。 当使用 SQL 解决问题时,也可以从 PL/SQL 程序执行 SQL 语句,而无需学习新的 API。 与其他过程编程语言一样,PL/SQL 允许您声明常量和变量、控制程序流、定义子程序和捕获运行时错误。 您可以将复杂的问题分解为易于理解的子程序,您可以在不同的应用程序中重复使用这些子程序。 • 错误处理 • 块 • 变量和常量 • 子程序 • 包 • 触发器 • 输入和输出 • 数据抽象 • 控制语句 • 一次处理查询结果集的一行 5 第 2 章 PL/SQL 概述 2.2.1 错误处理 PL/SQL 使检测和处理错误变得容易。 发生错误时,PL/SQL 会引发异常。正常执行停止并将控制转移到 PL/SQL 块的异常处理部分。您不必像在 C 程序中那样检查每个操作以确保其成功。 有关详细信息,请参阅异常处理 。 2.2.2 块 PL/SQL 源程序的基本单元是块,它对相关的声明和语句进行分组。 PL/SQL 块由关键字 DECLARE、BEGIN、EXCEPTION 和 END 定义。这些关键字将块分为声明部分、可执 行部分和异常处理部分。只有可执行部分是必需的。一个块可以有一个标签。 标签对块来说是局部区域的别名,并且在块完成执行时不再存在,有助于避免变量和子程序的命名空间混乱。 块可以嵌套:因为块是可执行语句,所以它可以出现在另一个块中,只要允许可执行语句。 您可以将块提交到交互式工具或将其嵌入到 KingbaseES 预编译器或 OCI 程序中。交互式工具或程序运行该块 一次。该块不存储在数据库中,因此,它被称为匿名块(即使它有标签)。 匿名块每次加载到内存时都会被编译一次,它的编译分为三个阶段: • 语法检查:检查 PL/SQL 语法,并生成解析树。 • 语义检查:对解析树进行类型检查和进一步处理。 • 代码生成:生成 PL/SQL 能识别的执行树 有关语法的详细信息,请参阅“块 ”。 示例 2-1 PL/SQL 块结构 此示例显示 PL/SQL 块的基本结构。 DECLARE -- Declarative part (optional) -- Declarations of local types, variables, & subprograms BEGIN -- Executable part (required) -- Statements (which can use items declared in declarative part) [EXCEPTION -- Exception-handling part (optional) -- Exception handlers for exceptions (errors) raised in executable part] END; 6 第 2 章 PL/SQL 概述 2.2.3 变量和常量 PL/SQL 允许您声明变量和常量,然后在可以使用表达式的任何地方使用它们。 随着程序的运行,变量的值可以改变,但常量的值不能。有关详细信息,请参阅“声明 ”和“为变量赋值 ”。 2.2.4 子程序 PL/SQL 子程序是一个命名的 PL/SQL 块,可以重复调用。 如果子程序有参数,则每次调用时它们的值可能不同。PL/SQL 有两种类型的子程序,过程和函数。一个函数返 回一个结果。 有关 PL/SQL 子程序的更多信息,请参阅“PL/SQL 子程序 ”。 2.2.5 包 包是一个模式对象,它对逻辑相关的 PL/SQL 类型、变量、常量、子程序、游标和异常进行分组。 一个包被存储在数据库中,许多应用程序可以访问并使用它的内容。您可以将包视为应用程序。 您可以编写自己的包——有关详细信息,请参阅PL/SQL 包。您还可以使用 KingbaseES 数据库提供的许多特定 产品的包。有关这些信息,请参阅《KingbaseES 插件参考手册》。 2.2.6 触发器 触发器是一个命名的 PL/SQL 单元,它存储在数据库中并响应数据库中发生的事件而运行。 您可以指定触发器是在事件之前还是之后触发,以及触发器是针对每个事件还是针对受事件影响的每一行运行。 例如,您可以创建每次 INSERT 语句影响 STUDENT 表时运行的触发器。 有关触发器的更多信息,请参阅PL/SQL 触发器。 2.2.7 输入和输出 大多数 PL/SQL 输入和输出 (I/O) 都是通过在数据库表中存储数据或查询这些表的 SQL 语句完成的。所有其他 PL/SQL I/O 都是使用 KingbaseES 数据库提供的 PL/SQL 包完成的。 表 2.2.1: I/O 处理包 包 描述 dbms_output 允许 PLSQL 块、子程序、包和触发器进行显示输出。 UTL_FILE 允许程序读写操作系统文件 7 第 2 章 PL/SQL 概述 2.2.8 数据抽象 通过数据抽象可以处理数据的基本属性,而无需过多地关注细节。 你可以先设计一个数据结构,然后再设计操作它的算法。 2.2.8.1 游标 游标是指向私有 SQL 区域的指针,该区域存储有关处理特定 SQL 语句或 PL/SQL SELECT INTO 语句的信 息。 您可以使用游标一次检索一行的结果集。您可以使用游标属性来获取有关游标状态的信息。例如,到目前为止该 语句影响了多少行。 有关游标的更多信息,请参阅“游标概述 ”。 2.2.8.2 复合变量 复合变量具有内部组件,您可以单独访问这些组件。 您可以将整个复合变量作为参数传递给子程序。PL/SQL 有两种复合变量:集合和记录。 在集合中,内部组件始终具有相同的数据类型,称为元素。您可以通过其唯一索引访问每个元素。Lists 和 arrays 是集合的经典示例。 在记录中,内部组件可以是不同的数据类型,称为字段。您可以通过名称访问每个字段。记录变量可以保存表格 行或表格行中的某些列。 有关复合变量的更多信息,请参阅“PL/SQL 集合和记录 ”。 2.2.8.3 使用%ROWTYPE 属性 %ROWTYPE 属性允许您声明表示数据库表或视图的完整或部分行的 record 变量。 对于整行或部分行的每一列,record 变量都有一个具有相同名称和数据类型的字段。如果行的结构发生变化,那 么记录的结构也会相应地发生变化。 有关%ROWTYPE 语法和语义的详细信息,请参阅“%ROWTYPE 属性 ”。 有关其用法的更多详细信息,请参阅“使用%ROWTYPE 属性声明 ”。 2.2.8.4 使用%TYPE 属性 %TYPE 属性允许您在不知道数据类型的情况下声明与先前声明的变量或列具有相同数据类型的数据对象(不知 道该对象的数据类型时)。 8 第 2 章 PL/SQL 概述 如果被引用的对象声明发生变化,那么引用对象的声明也会相应改变。%TYPE 属性在声明变量以保存数据库值 时特别有用。 有关%TYPE 语法和语义的详细信息,请参阅“%TYPE 属性 ”。 有关其用法的更多详细信息,请参阅“使用%TYPE 属性声明 ”。 2.2.8.5 抽象数据类型 抽象数据类型 (ADT) 由数据结构和操作数据的子程序组成。 构成数据结构的变量称为属性。操纵属性的子程序称为方法。 ADT 存储在数据库中。ADT 的实例可以存储在表中并用作 PL/SQL 变量。 ADT 让您可以通过将大型系统分成可以重用的逻辑组件来降低复杂性。 有关 ADT 的更多信息,请参阅“CREATE TYPE 语句 ”。 注意: ADT 也称为用户定义类型和对象类型。 2.2.9 控制语句 控制语句是 PL/SQL 语言对 SQL 语言最重要的扩展。 PL/SQL 有三类控制语句: • 条件选择语句,可让您针对不同的数据值运行不同的语句。 有关详细信息,请参阅“条件选择语句 ”。 • 循环语句,可让您使用一系列不同的数据值重复相同的语句。 有关详细信息,请参阅“循环语句 ”。 • 顺序控制语句,允许您转到指定的、标记的语句,或者什么都不做。 有关详细信息,请参阅“顺序控制语句 ”。 2.2.10 一次处理查询结果集的一行 PL/SQL 允许您发出 SQL 查询并一次处理结果集的一行。 您可以使用基本循环,也可以通过使用单独的语句来运行查询、检索结果并完成处理来精确控制过程。 示例 2-2 一次处理查询结果集的一行 此示例使用基本循环。 9 第 2 章 PL/SQL 概述 CREATE TABLE stduent(name text, score number); insert into stduent values ('xiaoming', 85.5),('xiaozhang', 89),('xiaohong', 92); \set SQLTERM / BEGIN FOR student IN (SELECT * FROM stduent WHERE score > 90) LOOP raise notice 'student: %', student; END LOOP; END; / 结果: NOTICE: student: (xiaohong,92) ANONYMOUS BLOCK 2.3 PL/SQL 的架构 对 PL/SQL 体系结构的基本了解对 PL/SQL 程序员很有帮助。 2.3.1 PL/SQL 引擎 PL/SQL 编译和运行系统是一个编译和运行 PL/SQL 单元的引擎。 该引擎可以安装在数据库中或应用程序开发工具中。 在任一环境中,PL/SQL 引擎都接受任何有效的 PL/SQL 单元作为输入。PL/SQL 引擎执行程序语句,但是会 将其中的 SQL 语句发送到数据库中的 SQL 引擎去执行再获取其结果。 通常,数据库处理 PL/SQL 单元。 当应用程序开发工具处理 PL/SQL 单元时,它会将它们传递给其本地 PL/SQL 引擎。如果 PL/SQL 单元不包含 SQL 语句,则本地引擎处理整个 PL/SQL 单元。如果应用程序开发工具需要条件和迭代控制,PL/SQL 单元会非常 有用。 2.3.2 PL/SQL 单元和编译参数 PL/SQL 单元受 PL/SQL 编译参数(一类数据库初始化参数)的影响。不同的 PL/SQL 单元。例如,包规范及 其主体, 可以有不同的编译参数设置。 PL/SQL 单元是以下之一: • PL/SQL ANONYMOUS BLOCK 10 第 2 章 PL/SQL 概述 • FUNCTION • PACKAGE • PACKAGE BODY • PROCEDURE • TRIGGER • TYPE • TYPE BODY 表 2.3.1: PL/SQL 编译参数 参数 描述 VARIABLE_CONFLICT 变量名和列名冲突时,如何处理: • PLSQL_RESOLVE_ERROR -- 报错 • PLSQL_RESOLVE_VARIABLE -- 解析成变量名(默认) • PLSQL_RESOLVE_COLUMN -- 解析成列名 PLSQL_CHECK_ASSERTS 是否执行 assert 检查语句: • false -- 不执行(默认) • true -- 执行 ORA_OPEN_CURSORS DBMS_SQL 中允许打开的游标数,最大 65535,最小值 0,默认值是 300 COMPILE_CHECKS 是否启用编译检查: • false -- 不启用(默认) • true -- 启用 11 第 3 章 PL/SQL 语言基础 3 第 章 PL/SQL 语言基础 本章节阐述 PL/SQL 语言的基本组成。 • 字符集 • 词法单元 • 声明 • 对标识符的引用 • 标识符的作用域和可见性 • 为变量赋值 • 表达式 • 错误报告函数 3.1 字符集 任何要由 PL/SQL 处理或存储在数据库中的字符数据都必须表示为字节序列。单个字符的字节表示形式称为字 符码。一组字符码称为字符集。 每个数据库都支持一个数据库字符集和一个国家字符集。PL/SQL 也支持这些字符集。本章节解释了 PL/SQL 如何使用数据库字符集和国家字符集。 3.1.1 数据库字符集 PL/SQL 使用数据库字符集表示: • 存储 PL/SQL 单元的源文本; • 数据类型 CHAR、VARCHAR2、CLOB 和 LONG 的字符值; 12 第 3 章 PL/SQL 语言基础 数据库字符集可以是单字节,将每个支持的字符映射到一个特定的字节,也可以是多字节,宽度不同,将每个支 持的字符映射到一个由一个、两个、三个或四个字节组成的序列。字符码中的最大字节数取决于特定的字符集。 每个数据库字符集都包含以下基本字符: • 拉丁字母:A 到 Z 和 a 到 z • 十进制数字:0 到 9 • 标点符号 • 空白字符:空格、制表符、换行符和回车符 仅使用基本字符的 PL/SQL 源文本可以在任何数据库中存储和编译。使用非基本字符的 PL/SQL 源文本只能在 数据库字符集支持这些非基本字符的数据库中存储和编译。 表 3.1.1: 每个数据库字符集中的标点字符 标点 名称 ( 左括号 ) 右括号 < 小于号 > 大于号 + 加号 - 减号 * 乘号 / 除号 = 等于 , 逗号 ; 分号 : 冒号 . 句号 ! 叹号 ? 问号 ‘ 单引号 见续表 13 第 3 章 PL/SQL 语言基础 表 3.1.1 – 续表 3.1.2 标点 名称 “ 双引号 @ 艾特符号 % 百分号 # 井号 $ 美元符号 _ 下划线 丨 竖线 国际字符集 PL/SQL 使用国际字符集来表示数据类型 NCHAR、NVARCHAR 和 NCLOB 的字符值。 3.1.3 关于字符排序 排序规则是用于确定在比较和排序两个字符串时的一组规则,例如一个字符串是等于另一个字符串、在另一个字 符串之前还是在另一个字符串之后。 不同的排序对应不同语言的规则。排序规则敏感操作是比较文本并需要排序规则来控制比较规则的操作。 KingbaseES 数据库的 PL/SQL 的字符排序默认与 SQL 的字符排序规则一致。 3.2 词法单元 PL/SQL 的词法单位是其最小的单个组件,包括: • 分隔符 • 标识符 • 常量 • 编译指示 • 注释 • 词法单元之间的空白字符 14 第 3 章 PL/SQL 语言基础 3.2.1 分隔符 分隔符用于标记词法元素之间的分割,可以是一个字符,也可以是由多个字符组合组成的,都有特殊的意义。不 要在分隔符中嵌入任何其他字符(包括空格字符)。 表 3.2.1: PLSQL 分隔符 分隔符 含义 + 加法运算符 := 赋值运算符 % 属性标识 ’ 字符串分隔符 . 对象标识 || 连接运算符 / 除法运算符 ( 表达式或列表分隔符(开始) ) 表达式或列表分隔符(结束) , 项目分隔符 « 标签分隔符(开始) » 标签分隔符(结束) 多行注释分隔符(结束) * 乘法运算符 ” 带引号的标识符分隔符 .. 范围运算符 = 关系运算符(相等) <> 关系运算符(不相等) != 关系运算符(不相等) ~= 关系运算符(不相等) 见续表 15 第 3 章 PL/SQL 语言基础 表 3.2.1 – 续表 3.2.2 分隔符 含义 ^= 关系运算符(不相等) < 关系运算符(小于) >关系运算符(大于)<= 关系运算符(小于或等于) >= 关系运算符(大于或等于) -- 单行注释指示符 ; 语句终止符 - 减法运算符 ~ 求反运算符 标识符 标识符用于命名 PL/SQL 语法单元,包括: • 常量 • 游标 • 异常 • 关键字 • 标签 • 包 • 保留字 • 子程序 • 类型 • 变量 标识符中的每个字符都是有意义的,例如 lastname 和 last_name 是不同的。 必须通过一个或多个空白或者一个标点符号来分隔相邻的标识符。 标识符的大小写是不敏感的。例如 lastname、Lastname、LASTNAME 是相同的。 16 第 3 章 PL/SQL 语言基础 3.2.2.1 保留关键字和非保留关键字 PL/SQL 中有一些具有特殊意义的标识符,被称作保留关键字和非保留关键字。 不能使用保留关键字作为用户定义的标识符。 可以用非保留关键字作为用户定义的标识符,但不建议这样做。 3.2.2.2 用户自定义标识符 用户自定义标识符: • 由数据库字符集中的字符组成 • 一般用户自定义标识符或带引号的用户自定义标识符 3.2.2.2.1 普通自定义用户标识符 普通用户定义标识符: • 字母或下划线开头 • 可以包括字母、数字和以下符号: – 美元符号($) – 数字符号(#) – 下划线(_) • 不是保留字 数据库字符集定义了哪些字符被分类为字母和数字。例如以下是可接受的标识符: X t2 credit_limit LastName kes$number money$$$tree try_again_ PL/SQL 不允许在标识符中使用分隔符,如下所示: mine&yours -- 不允许使用符号(&) debit-amount -- 不允许使用连字符(-) on/off -- 不允许使用斜杠(/) user id -- 空格是不允许的 17 第 3 章 PL/SQL 语言基础 3.2.2.2.2 带引号的用户自定义标识符 引用用户定义标识符用双引号括起来。 在双引号之间,除双引号、换行符和空字符外,允许使用数据库字符集中的任何字符。例如,这些标识符是可以 接受的: "X+Y" "last name" "on/off switch" "employee(s)" "*** header info ***" KingbaseES 数据库 PL/SQL 中带引号的用户定义标识符任何情况下都不区分大小写: • 如果带引号的用户定义标识符(不包含双引号)是有效的普通用户自定义标识符,则双引号在引用该标识符时 是可选的。 • 可以使用保留字作为引用的用户定义标识符,但不建议这样做。因为保留字不是有效的普通用户自定义标识 符,所以必须始终将标识符用双引号括起来。 示例 3-1 对不带引号的不区分大小写的用户自定义的标识符的有效引用 在本例中,带引号的用户自定义标识符“HEI”在没有双引号的情况下是一个有效的一般用户自定义标识符。因 此,Hei 的引用用法是有效的。 \set SQLTERM / DECLARE "HEI" varchar2(10) := 'hei'; BEGIN RAISE NOTICE '%',HEI; END; / 结果: NOTICE: hei 示例 3-2 对带引号的不区分大小写的用户自定义的标识符的无效引用 在本例中,引用“Hei”有效,因为 KingbaseES 下的 PL/SQL 标识符不区分大小写。 \set SQLTERM / DECLARE "HEI" varchar2(10) := 'hei'; BEGIN RAISE NOTICE '%',"Hei"; END; / 18 第 3 章 PL/SQL 语言基础 结果: NOTICE: hei 示例 3-3 引用保留字关键字作为用户定义的标识符 本例中第一个示例将保留字 END 加双引号后“END”,可以作为用户定义的标识符使用,引用该标识符时,不 区分大小写;第二个示例定义了“END”和“End”两个变量,但因为不区分大小写,所以认为是同一个变量,运行 结果提示报错。 \set SQLTERM / DECLARE "END" varchar2(15) := 'UPPERCASE'; BEGIN RAISE NOTICE '%', "END"; RAISE NOTICE '%', "End"; RAISE NOTICE '%', "end"; END; / 结果: UPPERCASE UPPERCASE UPPERCASE \set SQLTERM / DECLARE "END" varchar2(15) := 'UPPERCASE'; "End" varchar2(15) := 'Initial Capital'; BEGIN RAISE NOTICE '%', "END"; RAISE NOTICE '%', "End"; END; / 结果: ERROR: duplicate declaration at or near ""End"" LINE 3: "End" varchar2(15) := 'Initial Capital'; 示例 3-4 忽略双引号 本例引用了一个带引号的用户定义标识符,该标识符是一个保留字,但忽略了将其括在双引号中。 \set SQLTERM / DECLARE 19 第 3 章 PL/SQL 语言基础 "HEI" varchar2(10) := 'hei'; -- HEI is not a reserved word "END" varchar2(10) := 'end'; -- END is a reserved word BEGIN RAISE NOTICE '%', Hei; -- Double quotation marks are optional RAISE NOTICE '%', END; -- Double quotation marks are required end; / 结果: ERROR: syntax error at or near "END" LINE 6: 3.2.3 RAISE NOTICE '%', END; -- Double quotation marks are r... 常数 常数就是一个数字、字符、字符串、或布尔值。它本身是数据不是对数据的引用。常数也不是用标识符来标识, 当然也不是计算出来的。例,123 是整数常数,‘abc’是字符常数,而 1+2 不是常数。 PL/SQL 常数包括所有 SQL 常数和布尔文本(SQL 没有)。布尔常数是预定义的逻辑值 TRUE、FALSE 或 NULL。NULL 表示未知值。 在 PL/SQL 中使用字符常量,需注意: • 字符常量是大小写敏感的。 例如,‘Z’和‘z’是不同的。 • 空白字符也是有意义的。 例如,这些都是不同的: 'abc' ' abc' 'abc ' ' abc ' 'a b c' • PL/SQL 中没有用来表示“此字符串与下一个源代码行上的字符串是同一个”的行连接符。如果在下一行代码 行存在字符串,则该字符串包含换行符。 例如,以下 PL/SQL 代码: set serverout on \set SQLTERM / BEGIN RAISE NOTICE '%', 'Test string' || chr(10) || ' wrapping.'; 20 第 3 章 PL/SQL 语言基础 END; / 结果: Test string wrapping. 如果字符串一行内放不下,打印不希望包含换行符,则使用字符串连接运算符(||)构造字符串。 例如,以下 PL/SQL 代码: set serverout on \set SQLTERM / BEGIN RAISE NOTICE '%', 'Test string' || 'concatenation'); END; / 结果: test string concatenation. • ‘0’到‘9’不等同于整数常数 0 到 9。 但是,由于 PL/SQL 将它们转换为整数,因此可以在算术表达式中使用它们。 • 零字符的字符常数的值为 NULL,称为 NULL 字符串。 但是,此 NULL 值不是布尔值 NULL。 • 普通字符常数由数据库字符集中的字符组成。 • 国家字符常数由国家字符集中的字符组成。 3.2.4 编译指令 编译指令(PRAGMA)是编译器在编译时处理的一条指令。 一条编译指令以保留字 PRAGMA 开头,后跟编译指令的名称。有些编译指令有参数。编译指令可以出现在声明 或语句之前。其他限制可能适用于特定编译指令。编译指令影响的程度取决于编译指令本身。编译器无法识别其名称 或参数的编译指令无效。 3.2.5 注释 PL/SQL 编译器忽略注释。向程序添加注释可以增强可读性。通常,使用注释来描述每个代码段的用途,也可以 将代码通过注释禁用。 21 第 3 章 PL/SQL 语言基础 3.2.5.1 单行注释 单行注释开始于“--”,一直到行尾结束。 • 对于函数、存储过程、程序包、触发器等 sql 语句定义中,“--”单行注释只保留 AS 到 END 间的。 • 对于 TYPE BODY 语句定义中,“--”单行注释只保留语句内的函数、存储过程等的 AS 到 END 间的。 测试或调试程序时,可以通过单行注释禁用一行代码,例如: -- DELETE FROM employees WHERE comm_pct IS NULL 示例 3-5 单行注释 这个例子有 3 处单行注释。 \set SQLTERM / DECLARE num_func NUMBER; nums NUMBER; BEGIN -- Begin processing SELECT COUNT(*) INTO nums FROM PG_PROC WHERE PRONAME = 'f1'; -- Check number of function f1 num_func := nums; -- Compute another value END; / 3.2.5.2 多行注释 可以注释多行代码,/*注释内容*/。 您可以使用多行注释分隔符“注释掉”代码部分。 例如: /* IF 1 THEN num1 := 1; END IF; */ 示例 3-6 多行注释 22 第 3 章 PL/SQL 语言基础 \set SQLTERM / DECLARE num1 number; num2 number; BEGIN IF 1 THEN num1 := 1; END IF; num2 = 2; raise notice 'num1 = %,num2 = %', num1, num2; END; / 结果: NOTICE: num1 = 1,num2 = 2 3.2.6 词法单元之间的空白字符 您可以在词法单元之间放置空白字符,这通常会使源代码文本更易于阅读。 示例 3-7 空白字符,提高代码可读性 \set SQLTERM / DECLARE a NUMBER := 10; b NUMBER := 5; min NUMBER; BEGIN IF a 示例 3-15 声明与另一个变量类型相同的变量 在本例中,变量 surname 继承变量 name 的数据类型、大小。因为 surname 不继承 name 的约束条件,所以它 的声明可指定初始值也可以不指定。 \set SQLTERM / DECLARE name VARCHAR(25) NOT NULL := 'Smith'; surname name%TYPE := 'Jones'; BEGIN RAISE NOTICE 'name= %', name; RAISE NOTICE 'surname=%',surname; END; / 结果: 27 第 3 章 PL/SQL 语言基础 name=Smith surname=Jones 3.4 对标识符的引用 引用标识符时,使用的名称可以是简单的、限定的、远程的,也可以是限定的和远程的。 标识符的简单名称是其声明中的名称。 例如: \set SQLTERM / DECLARE b INTEGER; -- Declaration BEGIN b := 1; -- Reference with simple name END; / 如果在已经命名的 PL/SQL 单元里声明了标识符,则可以(有时必须)使用限定名来引用他。使用方法如下: unit_name.simple_identifier_name 例如,如果包 p 声明标识符 a,则可以使用限定名称 p.a 引用标识符。单元名称也可以(有时必须)限定。当标 识符不可见时,必须对其进行限定。 3.5 标识符的作用域和可见性 标识符的作用域是 PL/SQL 单元中可以引用标识符的区域。标识符的可见性是 PL/SQL 单元中可以引用标识符 而无需对其进行限定的区域。标识符是声明它的 PL/SQL 单元的本地标识符。如果该单元有子单元,那么标识符对 它们来说是全局的。 如果子单元重新声明了一个全局标识符,那么在子单元内部,两个标识符都在作用域内,但只有本地标识符可 见。要引用全局标识符,子单元必须使用声明它的单元的名称来限定它。如果该单元没有名称,则子单元无法引用全 局标识符。 PL/SQL 单元不能引用在同一级别的其他单元中声明的标识符,因为这些标识符对于程序块来说既不是局部的, 也不是全局的。 不能在同一 PL/SQL 单元中两次声明同一标识符。如果这样做,则在引用重复标识符时会发生错误。 可以用两个不同的 PL/SQL 单元声明相同的标识符。标识符表示的两个对象是不同的。改变一个不会影响另一 个。 28 第 3 章 PL/SQL 语言基础 在同一作用域内,为标签和子程序指定唯一的名称,以避免混淆和意外结果。 示例 3-16 标识符的作用域和可见性 本例显示了几个标识符的作用域和可见性。第一个子块重新声明全局标识符 a。要引用全局变量 a,第一个子块 必须使用外部块的名称对其进行限定,但外部块没有名称。因此,第一子块不能引用全局变量 a;它只能引用其局部 变量 a。由于子块处于同一级别,第一个子块不能引用 d,第二个子块不能引用 c。 \set SQLTERM / -- Outer block: DECLARE a TEXT; -- Scope of a (CHAR) begins b INT; -- Scope of b begins BEGIN -- Visible: a (CHAR), b -- First sub-block: DECLARE a INT; -- Scope of a (INTEGER) begins c NUMBER; -- Scope of c begins BEGIN -- Visible: a (INTEGER), b, c NULL; END; -- Scopes of a (INTEGER) and c end -- Second sub-block: DECLARE d VARCHAR; -- Scope of d begins BEGIN -- Visible: a (CHAR), b, d NULL; END; -- Scope of d ends -- Visible: a (CHAR), b END; -- Scopes of a (CHAR) and b end / 示例 3-17 使用块标签限定重新声明的全局标识符 此示例使用名称 lable1 标记块。因此,在子块重新声明全局变量 num 后,它可以通过使用块标签限定其名称来 引用该全局变量。子块还可以通过其简单名称引用其局部变量 num。 \set SQLTERM / BEGIN <> -- label DECLARE num number := 1.1; BEGIN DECLARE num number := 1.2; 29 第 3 章 PL/SQL 语言基础 BEGIN raise notice 'num = %', num; raise notice 'lable1.num = %', lable1.num; END; END; END; / 结果: NOTICE: num = 1.2 NOTICE: lable1.num = 1.1 示例 3-18 用子程序名限定标识符 在本例中,extenal_pro 过程声明了一个变量 num 和一个函数 sub_pro 。函数内部又重新声明变量 num 。然后 函数通过使用 extenal_pro 过程名限定来引用全局变量 num 。 \set SQLTERM / CREATE OR REPLACE PROCEDURE extenal_pro (num_max NUMBER) AS num NUMBER := 3; FUNCTION sub_pro RETURN BOOLEAN IS num NUMBER := 1; over_limit BOOLEAN; BEGIN IF extenal_pro.num <= num_max THEN -- reference global variable over_limit := FALSE; ELSE over_limit := TRUE; num := num_max; -- reference local variable END IF; RETURN over_limit; END sub_pro; BEGIN IF sub_pro THEN raise notice 'over_limit'; ELSE raise notice 'not over_limit'; END IF; END; / \set SQLTERM / BEGIN extenal_pro(1); END; / 30 第 3 章 PL/SQL 语言基础 结果: NOTICE: over_limit 示例 3-19 同一作用域内的重复声明的标识符 不能在同一 PL/SQL 单元中两次声明同一标识符。如果这样做,则在声明重复标识符时会发生错误,如本例所 示。 \set SQLTERM / DECLARE num number; num int; -- duplicate identifier BEGIN null; END; / 结果: ERROR: duplicate declaration at or near "num" LINE 3: num int; -- duplicate identifier 示例 3-20 在不同的单元中声明相同的标识符 可以在两个不同的 PL/SQL 单元内声明相同的标识符。标识符表示的两个对象是不同的。如本例所示,更改一 个不会影响另一个。在同一作用域内,为标签和子程序指定唯一的名称,以避免混淆和意外结果。 \set SQLTERM / DECLARE PROCEDURE a IS x VARCHAR2(1); BEGIN x := 'a'; -- Assign the value 'a' to x RAISE NOTICE 'procedure p, x = %', x; END; PROCEDURE b IS x VARCHAR2(1); BEGIN x := 'b'; -- Assign the value 'b' to x RAISE NOTICE 'procedure p, x = %', x; END; BEGIN a; b; 31 第 3 章 PL/SQL 语言基础 END; / 结果: NOTICE: procedure p, x = a NOTICE: procedure p, x = b 示例 3-21 同一作用域内具有相同名称的标签和子程序 在本例中,block 是块和子程序的名称。块和子程序都声明了一个名为 a 的变量。在子程序中,block.a 指的是局 部变量 a,而不是全局变量 a。 \set SQLTERM / BEGIN <> DECLARE a NUMBER := 5; PROCEDURE block AS a NUMBER := 0; BEGIN RAISE NOTICE 'a = %', a; RAISE NOTICE 'block.a = %', block.a; END; BEGIN block; END; END; / 结果: NOTICE: a = 0 NOTICE: block.a = 0 3.6 为变量赋值 声明变量后,可以通过以下方式为其赋值: • 使用赋值语句将表达式赋值给变量。 • 使用 SELECT INTO 或 FETCH 语句从 table 中赋值给变量。 • 将其作为 OUT 或 IN-OUT 参数传递给子程序,然后在子程序内部赋值。 32 第 3 章 PL/SQL 语言基础 变量和值必须具有兼容的数据类型。如果一种数据类型可以隐式转换为另一种数据类型,那么它与另一种数据类 型兼容。 3.6.1 用赋值语句为变量赋值 要将表达式的值赋给变量,请使用以下形式的赋值语句: variable_name := expression; 示例 3-22 使用赋值语句为变量赋值 本例声明几个变量(为某些变量指定初始值),然后使用赋值语句将表达式的值赋值给它们。 \set SQLTERM / DECLARE -- You can assign initial values here name text; score number; valid_id BOOLEAN; stu_rec1 student%ROWTYPE; stu_rec2 student%ROWTYPE; TYPE ass_tab IS TABLE OF NUMBER INDEX BY PLS_INTEGER; ass_tab_var ass_tab; BEGIN -- You can assign values here too name := 'xx'; score := 99; valid_id := TRUE; stu_rec1.name := name; stu_rec2.score := score; stu_rec2 := stu_rec1; ass_tab_var(5) := 0.2 * score; END; / 3.6.2 用 SELECT INTO 语句为变量赋值 SELECT INTO 语句的一种简单形式是: SELECT select_item [, select_item ]... INTO variable_name [, variable_name ]... FROM table_name; 对于每个 select_item,必须有一个对应的、类型兼容的 variable_name。因为 SQL 没有布尔类型,所以 variable_name 不能是布尔变量。 示例 3-23 使用 SELECT INTO 语句为变量赋值 33 第 3 章 PL/SQL 语言基础 本例使用 SELECT INTO 语句将姓名为’xx’ 的学生的得分的 20% 赋值给变量 score。 \set SQLTERM / DECLARE score NUMBER; BEGIN SELECT score * 0.2 INTO score FROM student WHERE name = 'xx'; raise notice 'score = %',score; END; / 结果: NOTICE: score = 19.8 3.6.3 将值作为子程序的参数分配给变量 如果将变量作为 OUT 或 IN OUT 参数传递给子程序,并且子程序为该参数指定了一个值,则该变量将在子程序 完成运行后保留该值。 示例 3-24 将变量赋值为 IN-OUT 子程序参数 本例将变量 score 传递给过程 adjust_score。该过程为相应的形式参数 score 赋值。因为 score 是一个 IN OUT 参数,所以变量 score 会在过程运行结束后保留赋给的值。 \set SQLTERM / DECLARE score NUMBER; PROCEDURE adjust_score ( score IN OUT NUMBER, adjustment NUMBER ) IS BEGIN score := score + adjustment; END; BEGIN SELECT score INTO score FROM student WHERE name = 'xx'; raise notice 'before invoking procedure , score = %', score; adjust_score (score, 0.5); raise notice 'before invoking procedure , score = %', score; END; 34 第 3 章 PL/SQL 语言基础 / 结果: NOTICE: before invoking procedure , score = 99 NOTICE: before invoking procedure , score = 99.5 3.6.4 给 BOOLEAN 变量赋值 只有值 TRUE、FALSE 和 NULL 可以给布尔变量赋值。 示例 3-25 给 BOOLEAN 变量赋值 这个示例布尔变量 stop 默认初始化为 NULL,后将其赋值为 FALSE,并与 TRUE 进行比较,用一个 BOOLEAN 表达式的值给其赋值。 \set SQLTERM / DECLARE stop BOOLEAN; num NUMBER := 0; BEGIN stop := FALSE; WHILE stop != TRUE LOOP num := num + 1; stop := (num > 500); END LOOP; END; / 3.7 表达式 表达式是一个或多个值、运算符和 SQL 函数的组合,其计算结果为一个值。 一个表达式始终返回唯一值。按照复杂性的增加顺序,最简单的表达式是: • 单个常量或变量(例如,a) • 一元操作符和单个操作数(例如,-a) • 二元操作符和 2 个操作数(例如,a+b) 操作数可以是变量、常量、常数、运算符、函数调用、占位符或其他表达式。因此,表达式可以任意复杂。 35 第 3 章 PL/SQL 语言基础 操作数的数据类型决定表达式的数据类型。每次计算表达式时,都会生成该数据类型的单个值。该结果的数据类 型就是表达式的数据类型。 3.7.1 连接运算符 连接运算符(||)将一个字符串操作数附加到另一个字符串操作数。连接运算符忽略 NULL 操作数。 示例 3-26 连接运算符 \set SQLTERM / DECLARE a text := 'hello'; b text := ' world'; BEGIN raise notice '%', a || b; END; / 结果: NOTICE: hello world 示例 3-27 带 NULL 操作数的连接运算符 如本例所示,连接运算符忽略 NULL 操作数。 \set SQLTERM / BEGIN RAISE NOTICE '%', 'hello' || NULL || NULL || ' world'; END; / 结果: NOTICE: hello world 3.7.2 运算符优先级 运算是一元运算符操作及单个操作数或二元运算符及其两个操作数。表达式中的操作按照运算符的优先级的顺序 计算。 下表显示了从最高到最低的运算符优先级。优先级相等的运算符不会按特定顺序求值。 36 第 3 章 PL/SQL 语言基础 运算符 含义 ^ 指数运算符 +,- 正、负 /,* 除、乘 +,-,|| 加、减、连接符 =,<,>,<=,>=,<>,!=,^=,IS NULL,LIKE,BETWEEN,IN 比较运算符 NOT 非 AND 与 OR 或 要控制计算顺序,请将操作包括在括号中。 嵌套括号时,首先计算嵌套最深的操作。 如果括号不影响计算顺序,还可以使用括号来提高可读性。 示例 3-28 用括号控制求值顺序 在本例中,首先计算运算(2+3)和(4+5),分别产生值 5 和 9。接下来,计算操作 5×9,产生结果 45。最 后,对操作 45/9 进行计算,得出最终值 5。 \set SQLTERM / DECLARE x int := ((2+3)*(4+5))/9; BEGIN raise notice 'x = %',x; END; / 结果: NOTICE: x = 5 示例 3-29 使用括号提高可读性 在本例中,括号不影响求值顺序。它们只会提高可读性。 \set SQLTERM / DECLARE x int := 1^2*2^2; y int := (1^2)*(2^2); BEGIN 37 第 3 章 PL/SQL 语言基础 raise notice 'x = %',x; raise notice 'y = %',y; END; / 结果: NOTICE: x = 4 NOTICE: y = 4 示例 3-30 运算符优先级 本例显示了运算符优先级和括号在几个更复杂的表达式中的效果。 \set SQLTERM / DECLARE score NUMBER := 100; additional_score NUMBER := 20; BEGIN -- Division has higher precedence than addition: RAISE NOTICE '3 + 10 / 2 = %', 3 + 10 / 2; RAISE NOTICE '10 / 2 + 3 = %', 10 / 2 + 3; -- Parentheses override default operator precedence: RAISE NOTICE '6 + 9 / 3 = %', 6 + 9 / 3; RAISE NOTICE '(6 + 9) / 3 = %', (6 + 9) / 3; -- Most deeply nested operation is evaluated first: RAISE NOTICE '80 + (10 / 2 + (6 - 2)) = %', 80 + (10 / 2 + (6 - 2)); -- Parentheses, even when unnecessary, improve readability: RAISE NOTICE '(score * 0.5) + (additional_score * 0.2) = %', (score * 0.5) + (additional_score * 0.2); RAISE NOTICE 'score * 0.5 + additional_score * 0.2 = %', score * 0.5 + additional_score * 0.2; END; / 结果: NOTICE: 3 + 10 / 2 = 8 NOTICE: 10 / 2 + 3 = 8 NOTICE: 6 + 9 / 3 = 9 NOTICE: (6 + 9) / 3 = 5 NOTICE: 80 + (10 / 2 + (6 - 2)) = 89 NOTICE: (score * 0.5) + (additional_score * 0.2) = 54.0 NOTICE: score * 0.5 + additional_score * 0.2 = 54.0 38 第 3 章 PL/SQL 语言基础 3.7.3 逻辑运算符 逻辑运算符 AND、OR 和 NOT 遵循下表所示的三态逻辑。AND 和 OR 是二元运算符;NOT 是一元运算符。 表 3.7.1: 逻辑真值表 x y x AND y x OR y NOT x TRUE FALSE FALSE TRUE FALSE TRUE TRUE TRUE TRUE FALSE TRUE NULL NULL TRUE FALSE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE FALSE TRUE FALSE NULL FALSE NULL TRUE NULL TRUE NULL TRUE NULL NULL FALSE FALSE NULL NULL NULL NULL NULL NULL NULL 当且仅当两个操作数为 TURE,AND 返回 TURE;当有一个为 TURE,OR 返回 TURE;NOT 返回操作数的相 反值,除非操作数是 NULL。NOT NULL 还是 NULL。因为 NULL 本身就是一个不确定的值。 示例 3-31 打印布尔变量的过程程序 这个例子创建一个 show_boolean 程序,它输出一个 BOOLEAN 变量的值。该过程使用“IS [NOT] NULL 运算 符”。本章中的几个示例调用 show_boolean。 \set SQLTERM / CREATE OR REPLACE PROCEDURE show_boolean ( name VARCHAR2, value BOOLEAN ) AUTHID DEFINER IS BEGIN IF value IS NULL THEN RAISE NOTICE '% = NULL',name; ELSIF value = TRUE THEN RAISE NOTICE '% = TRUE',name; ELSE RAISE NOTICE '% = FALSE',name; END IF; END; / 39 第 3 章 PL/SQL 语言基础 示例 3-32 AND 运算符 如本例所示,当且仅当两个操作数均为 TRUE 时,AND 返回 TRUE。 \set SQLTERM / DECLARE PROCEDURE show_x_and_y ( x BOOLEAN, y BOOLEAN ) IS BEGIN show_boolean ('x', x); show_boolean ('y', y); show_boolean ('x AND y', x AND y); END show_x_and_y; BEGIN show_x_and_y (FALSE, FALSE); show_x_and_y (TRUE, FALSE); show_x_and_y (FALSE, TRUE); show_x_and_y (TRUE, TRUE); show_x_and_y (TRUE, NULL); show_x_and_y (FALSE, NULL); show_x_and_y (NULL, TRUE); show_x_and_y (NULL, FALSE); END; / 结果: NOTICE: x = FALSE NOTICE: y = FALSE NOTICE: x AND y = FALSE NOTICE: x = TRUE NOTICE: y = FALSE NOTICE: x AND y = FALSE NOTICE: x = FALSE NOTICE: y = TRUE NOTICE: x AND y = FALSE NOTICE: x = TRUE NOTICE: y = TRUE NOTICE: x AND y = TRUE NOTICE: x = TRUE NOTICE: y = NULL NOTICE: x AND y = NULL NOTICE: x = FALSE NOTICE: y = NULL NOTICE: x AND y = FALSE 40 第 3 章 PL/SQL 语言基础 NOTICE: x = NULL NOTICE: y = TRUE NOTICE: x AND y = NULL NOTICE: x = NULL NOTICE: y = FALSE NOTICE: x AND y = FALSE 示例 3-33 OR 运算符 如本例所示,如果其中一个操作数为 TRUE,OR 返回 TRUE。 \set SQLTERM / DECLARE PROCEDURE show_x_or_y ( x BOOLEAN, y BOOLEAN ) IS BEGIN show_boolean ('x', x); show_boolean ('y', y); show_boolean ('x OR y', x OR y); END show_x_or_y; BEGIN show_x_or_y (FALSE, FALSE); show_x_or_y (TRUE, FALSE); show_x_or_y (FALSE, TRUE); show_x_or_y (TRUE, TRUE); show_x_or_y (TRUE, NULL); show_x_or_y (FALSE, NULL); show_x_or_y (NULL, TRUE); show_x_or_y (NULL, FALSE); END; / 结果: NOTICE: x = FALSE NOTICE: y = FALSE NOTICE: x OR y = FALSE NOTICE: x = TRUE NOTICE: y = FALSE NOTICE: x OR y = TRUE NOTICE: x = FALSE NOTICE: y = TRUE NOTICE: x OR y = TRUE NOTICE: x = TRUE 41 第 3 章 PL/SQL 语言基础 NOTICE: y = TRUE NOTICE: x OR y = TRUE NOTICE: x = TRUE NOTICE: y = NULL NOTICE: x OR y = TRUE NOTICE: x = FALSE NOTICE: y = NULL NOTICE: x OR y = NULL NOTICE: x = NULL NOTICE: y = TRUE NOTICE: x OR y = TRUE NOTICE: x = NULL NOTICE: y = FALSE NOTICE: x OR y = NULL 示例 3-34 NOT 运算符 如本例所示,NOT 返回与其操作数相反的值,除非操作数为 NULL。NOT NULL 返回 NULL,因为 NULL 是 一个不确定的值。 \set SQLTERM / DECLARE PROCEDURE show_not_x ( x BOOLEAN ) IS BEGIN show_boolean ('x', x); show_boolean ('NOT x', NOT x); END show_not_x; BEGIN show_not_x (TRUE); show_not_x (FALSE); show_not_x (NULL); END; / 结果: NOTICE: x = TRUE NOTICE: NOT x = FALSE NOTICE: x = FALSE NOTICE: NOT x = TRUE NOTICE: x = NULL NOTICE: NOT x = NULL 示例 3-35 不相等比较中的 NULL 值 42 第 3 章 PL/SQL 语言基础 在本例中,您可能期望语句序列运行,因为 x 和 y 看起来不相等。但是,NULL 值是不确定的,x 是否等于 y 是 未知的。因此,IF 条件会产生 NULL,并且会绕过语句序列。 \set SQLTERM / DECLARE x NUMBER := 5; y NUMBER := NULL; BEGIN IF x != y THEN -- yields NULL, not TRUE RAISE NOTICE 'x != y'; ELSIF x = y THEN -- also yields NULL RAISE NOTICE 'x = y'; ELSE RAISE NOTICE 'Can''t tell if x and y are equal or not.'; END IF; END; / 结果: NOTICE: Can't tell if x and y are equal or not. 示例 3-36 相等比较中的 NULL 值 在本例中,a 和 b 看起来是相等的。但是,结果也未知的,因为 IF 条件产生了 NULL ,所以语句序列就被绕过 去不执行了。 \set SQLTERM / DECLARE a NUMBER := NULL; b NUMBER := NULL; BEGIN IF a = b THEN -- yields NULL, not TRUE RAISE NOTICE 'a = b'; -- not run ELSIF a != b THEN -- yields NULL, not TRUE RAISE NOTICE 'a != b'; -- not run ELSE RAISE NOTICE 'Can''t tell if two NULLs are equal'; END IF; END; / 结果: NOTICE: Can't tell if two NULLs are equal 示例 3-37 NOT NULL 等于 NULL 43 第 3 章 PL/SQL 语言基础 在本例中,两个 IF 语句似乎是等价的。但是,如果 a 或 b 不是确定的值 2 和 5,而是其中有一个值是 NULL, 那么第一个 IF 语句将 b 的值赋值给 high,而第二个 if 语句将 a 的值赋值为 high。 \set SQLTERM / DECLARE a INTEGER := 2; b INTEGER := 5; high INTEGER; BEGIN IF (a > b) THEN high := a; ELSE high := b; END IF; RAISE NOTICE 'high = %',high; IF NOT (a > b) THEN high := b; ELSE high := a; END IF; RAISE NOTICE 'high = %',high; END; / 结果 NOTICE: high = 5 NOTICE: high = 5 示例 3-38 更改逻辑运算符的求值顺序 本例三次调用 show_boolean 过程。第三次调用和第一次调用在逻辑上是等价的。第三次调用中的括号只会提高 可读性。第二次调用中的括号改变了操作顺序。 \set SQLTERM / DECLARE a BOOLEAN := FALSE; b BOOLEAN := FALSE; BEGIN show_boolean ('NOT a AND b', NOT a AND b); show_boolean ('NOT (x AND y)', NOT (a AND b)); show_boolean ('(NOT x) AND y', (NOT a) AND b); END; / 结果: 44 第 3 章 PL/SQL 语言基础 NOTICE: NOT a AND b = FALSE NOTICE: NOT (x AND y) = TRUE NOTICE: (NOT x) AND y = FALSE 3.7.4 短路计算 当计算一个逻辑表达式的时候,PL/SQL 使用短路计算。一旦能够确认表达式的值,则就不进行后续计算了。因 此,您可以编写可能会导致错误的表达式。 示例 3-39 短路计算 在本例中,短路计算可防止 OR 表达式导致除零错误。当 num1 的值为零时,左操作数的值为真,因此 PL/SQL 不会计算右操作数。如果 PL/SQL 在应用 OR 运算符之前对两个操作数进行了求值,则正确的操作数将导致除零错 误。 \set SQLTERM / DECLARE num1 INTEGER := 0; num2 INTEGER := 100; BEGIN IF (num1 = 0) OR ((num2 / num1) < 5) THEN RAISE NOTICE 'On hand quantity is zero.'; END IF; END; / 结果: NOTICE: On hand quantity is zero. 3.7.5 比较运算符 比较运算符将一个表达式与另一个表达式进行比较。结果总是 TRUE、FALSE 或 NULL。如果一个表达式的值 为 NULL,则比较结果也为 NULL。比较运算符包括: • IS [NOT] NULL 运算符 • 关系运算符 • LIKE 运算符 • BETWEEN 运算符 • IN 运算符 45 第 3 章 PL/SQL 语言基础 3.7.5.1 IS [NOT] NULL 运算符 IS NULL 运算符如果其操作数为 NULL,则返回布尔值 TRUE;如果不为 NULL,则返回 FALSE。IS NOT NULL 运算符的作用正好相反。 涉及 NULL 值的比较总是产生 NULL 值。 要测试值是否为 NULL,请使用 IF value IS NULL 形式做判断。 3.7.5.2 关系运算符 下表总结了关系运算符。 运算符 含义 = 等于 <>,!=,^= 不等于 < 小于 >大于<= 小于等于 >= 大于等于 3.7.5.2.1 算数比较 如果一个数字比另一个数字代表更大的数量,那么它就比另一个数字大。 真实数字被存储为近似值,所以建议比较它们是否相等。 示例 3-40 表达式中的关系运算符 本例调用 show_boolean 过程来打印使用关系运算符比较算术值的表达式的值。 \set SQLTERM / BEGIN show_boolean ('(1 + 1 = 2)', 1 + 1 = 2); show_boolean ('(1 + 1 <> 2)', 1 + 1 <> 2); show_boolean ('(1 + 1 != 2)', 1 + 1 != 2); show_boolean ('(1 + 1 ^= 2)', 1 + 1 ^= 2); show_boolean ('(2 < 4)', 2 < 4); show_boolean ('(2 > 4)', 2 > 4); show_boolean ('(2 <= 4)', 2 <= 4); show_boolean ('(2 >= 2)', 2 >= 2); 46 第 3 章 PL/SQL 语言基础 END; / 结果: NOTICE: (1 + 1 = 2) = TRUE NOTICE: (1 + 1 <> 2) = FALSE NOTICE: (1 + 1 != 2) = FALSE NOTICE: (1 + 1 ^= 2) = FALSE NOTICE: (2 < 4) = TRUE NOTICE: (2 > 4) = FALSE NOTICE: (2 <= 4) = TRUE NOTICE: (2 >= 2) = TRUE 3.7.5.2.2 布尔比较 根据定义,TRUE 大于 FALSE。任何与 NULL 的比较都返回 NULL。 3.7.5.2.3 字符比较 默认情况下,如果一个字符的二进制值大于另一个字符的二进制值,则这个字符大于另一个字符。 例如,这个比较是成立的: 'y' > 'r' 字符串需要逐个字符进行比较。例如,以下比较是成立的: 'Kathy' > 'Kathryn' 排序序列是字符集的内部顺序,其中一系列数字代码表示单个字符。如果一个字符的内部数值较大,则该字符的 值大于另一个字符的值。每种语言可能都有不同的规则来确定这些字符在排序序列中的位置。 3.7.5.2.4 时间比较 如果一个日期比另一个日期晚,那么这个日期比另一个日期大。 例如,以下表达式成立: '01-JAN-91' > '31-DEC-90' 47 第 3 章 PL/SQL 语言基础 3.7.5.3 LIKE 运算符 LIKE 运算符将字符、字符串或 CLOB 值与模式进行比较,如果值与模式匹配,则返回 TRUE,如果不匹配, 则返回 FALSE。 • 大小写是有意义的。 • 该模式可以包括两个通配符下划线(_)和百分号(%)。 • 下划线匹配一个字符。 • 百分号(%)匹配 0 个或更多字符。 要搜索百分号或下划线,请定义转义字符并将其置于百分号或下划线之前。 示例 3-41 表达式中的 LIKE 运算符 字符串“Student”与模式“S%u__n”匹配,但与“S%U_N”不匹配,如本例所示。 \set SQLTERM / DECLARE PROCEDURE string_match ( value VARCHAR2, pattern VARCHAR2 ) IS BEGIN IF value LIKE pattern THEN RAISE NOTICE 'TRUE'; ELSE RAISE NOTICE 'FALSE'; END IF; END; BEGIN string_match('Stduent', 'S%u__t'); string_match('Stduent', 'S%U__N'); END; / 结果: NOTICE: TRUE NOTICE: FALSE 示例 3-42 模式中的转义字符 本例使用反斜杠作为转义字符,因此字符串中的百分号不会用作通配符。 \set SQLTERM / DECLARE PROCEDURE escape_character (str VARCHAR2) IS 48 第 3 章 PL/SQL 语言基础 BEGIN IF str LIKE 'test1\%test2!' ESCAPE '\' THEN RAISE NOTICE 'TRUE'; ELSE RAISE NOTICE 'FALSE'; END IF; END; BEGIN escape_character('test1%test2!'); END; / 结果: NOTICE: TRUE 3.7.5.4 BETWEEN 运算符 BETWEEN 运算符测试值是否在指定范围内。a 和 b 之间的表达式 x 的值被定义为与表达式(x>=a)和 (x<=b)的值相同。表达式 x 将只计算一次。 示例 3-43 表达式中的 BETWEEN 运算符 本例调用 show_boolean 过程来打印包含 BETWEEN 运算符的表达式的值。 \set SQLTERM / BEGIN show_boolean ('4 BETWEEN 3 AND 5', 4 BETWEEN 3 AND 5); show_boolean ('3 BETWEEN 3 AND 4', 3 BETWEEN 3 AND 4); show_boolean ('5 BETWEEN 3 AND 4', 5 BETWEEN 3 AND 4); show_boolean ('4 BETWEEN 4 AND 5', 4 BETWEEN 4 AND 5); END; / 结果: NOTICE: 4 BETWEEN 3 AND 5 = TRUE NOTICE: 3 BETWEEN 3 AND 4 = TRUE NOTICE: 5 BETWEEN 3 AND 4 = FALSE NOTICE: 4 BETWEEN 4 AND 5 = TRUE 3.7.5.5 IN 运算符 IN 运算符测试集合成员资格。只有当 x 等于集合的一个成员时,x IN (set) 才返回 TRUE。 49 第 3 章 PL/SQL 语言基础 示例 3-44 表达式中的 IN 运算符 本例调用 show_boolean 过程来打印包含 IN 运算符的表达式的值。 \set SQLTERM / DECLARE chr VARCHAR2(1) := 'q'; BEGIN show_boolean ( 'chr IN (''i'', ''j'', ''k'')', chr IN ('i', 'j', 'k') ); show_boolean ( 'chr IN (''o'', ''p'', ''q'')', chr IN ('o', 'p', 'q') ); END; / 结果: NOTICE: chr IN ('i', 'j', 'k') = FALSE NOTICE: chr IN ('o', 'p', 'q') = TRUE 示例 3-45 带 NULL 值集合的 IN 运算符 本例将调用 show_boolean 过程,展示当 set 包含一个 NULL 值时会发生什么。 \set SQLTERM / DECLARE a INTEGER; -- Initialized to NULL by default b INTEGER := 5; c INTEGER := 10; BEGIN show_boolean ('5 IN (a, b, c)', 5 IN (a, b, c)); show_boolean ('5 NOT IN (a, b, c)', 5 NOT IN (a, b, c)); show_boolean ('10 IN (a, b)', 10 IN (a, b)); show_boolean ('10 NOT IN (a, b)', 10 NOT IN (a, b)); show_boolean ('a IN (a, b)', a IN (a, b)); show_boolean ('a NOT IN (a, b)', a NOT IN (a, b)); END; / 结果: NOTICE: 5 IN (a, b, c) = TRUE NOTICE: 5 NOT IN (a, b, c) = FALSE 50 第 3 章 PL/SQL 语言基础 NOTICE: 10 IN (a, b) = NULL NOTICE: 10 NOT IN (a, b) = NULL NOTICE: a IN (a, b) = NULL NOTICE: a NOT IN (a, b) = NULL 3.7.6 布尔表达式 布尔表达式是一个返回 TURE 或 FLASE 或 NULL 的表达式。一个简单的布尔表达式可以有布尔常数值、常 量、变量组成。常见形式如下: NOT boolean_expression boolean_expression relational_operator boolean_expression boolean_expression { AND | OR } boolean_expression 通常,在 PL/SQL 控制语句和 DML 语句的 WHERE 子句中使用布尔表达式作为条件。可以使用布尔变量本身 作为条件;您无需将其与值 TRUE 或 FALSE 进行比较。 示例 3-46 等价的布尔表达式 本例中,循环中的条件是等价的。 \set SQLTERM / DECLARE stop BOOLEAN; BEGIN stop := FALSE; WHILE stop = FALSE LOOP stop := TRUE; END LOOP; stop := FALSE; WHILE NOT (stop = TRUE) LOOP stop := TRUE; END LOOP; stop := FALSE; WHILE NOT stop LOOP stop := TRUE; END LOOP; END; / 51 第 3 章 PL/SQL 语言基础 3.7.7 CASE 表达式 CASE 表达式只从一个或多个方案中选择一个满足条件的执行,其他分支不执行。 3.7.7.1 简单的 CASE 表达式 一个简单的 CASE 表达式具有以下语法: CASE selector WHEN selector_value_1 THEN result_1 WHEN selector_value_2 THEN result_2 ... WHEN selector_value_n THEN result_n [ ELSE else_result ] END selector 是一个表达式(通常是一个变量)。每个 selector_value 和每个 result 可以是常数或表达式。至少一个 result 不能为常数 NULL。 简单的 CASE 表达式返回 selector_value 与 selector 匹配的第一个 result。其余的表达式不会被计算。如果没有 selector_value 与 selector 匹配,则 CASE 表达式将返回 else_result(如果存在),否则返回 NULL 值。 示例 3-47 简单 CASE 表达式 这个示例将一个简单 CASE 表达式的值赋给变量 comments,值是 Excellent 。 \set SQLTERM / DECLARE score CHAR(1) := 'A'; comments VARCHAR2(20); BEGIN comments := CASE score WHEN 'A' THEN 'Excellent' WHEN 'B' THEN 'Very Good' WHEN 'C' THEN 'Good' WHEN 'D' THEN 'Fair' WHEN 'F' THEN 'Poor' ELSE 'No such score' END; RAISE NOTICE 'score = %, comments = %', score, comments; END; / 结果: 52 第 3 章 PL/SQL 语言基础 NOTICE: score = A, comments = Excellent 示例 3-48 WHEN NULL 条件下的简单的 CASE 表达式 如果 selector 值为 NULL,则不能在 WHEN NULL 条件下被匹配,相反,可以在 WHEN boolean_expression IS NULL 条件下使用 CASE 表达式搜索。 \set SQLTERM / DECLARE score CHAR(1); -- NULL by default comments VARCHAR2(20); BEGIN comments := CASE score WHEN NULL THEN 'No score assigned' WHEN 'A' THEN 'Excellent' WHEN 'B' THEN 'Very Good' WHEN 'C' THEN 'Good' WHEN 'D' THEN 'Fair' WHEN 'F' THEN 'Poor' ELSE 'No such score' END; RAISE NOTICE 'score = %, comments = %', score, comments; END; / 结果: NOTICE: 3.7.7.2 score = , comments = No such score 搜索 CASE 表达式 搜索 CASE 表达式的语法: CASE WHEN boolean_expression_1 THEN result_1 WHEN boolean_expression_2 THEN result_2 ... WHEN boolean_expression_n THEN result_n [ ELSE else_result ] END 搜索 CASE 表达式返回第一个匹配 boolean_expression 为 TRUE 的 result。剩下的表达式不再计算。如果没有 boolean_expression 为 TRUE,则 CASE 表达式将返回 else_result(如果存在),否则返回 NULL 值。 53 第 3 章 PL/SQL 语言基础 示例 3-49 搜索 CASE 表达式 这个示例将搜索 CASE 表达式的值赋给变量 comments。 \set SQLTERM / DECLARE score CHAR(1) := 'A'; comments VARCHAR2(120); id NUMBER := 20220001; BEGIN comments := CASE WHEN score = 'F' THEN 'Poor' WHEN score = 'A' THEN 'Excellent' WHEN score = 'B' THEN 'Very Good' WHEN score = 'C' THEN 'Good' WHEN score = 'D' THEN 'Fair' ELSE 'No such score' END; RAISE NOTICE 'Result for student % is %',id, comments; END; / 结果: NOTICE: Result for student 20220001 is Excellent 示例 3-50 WHEN ... IS NULL 条件下的搜索 case 语句 本例使用一个搜索的 CASE 表达式来解决问题。 \set SQLTERM / DECLARE score CHAR(1); -- NULL by default comments VARCHAR2(20); BEGIN comments := CASE WHEN score IS NULL THEN 'No score assigned' WHEN score = 'A' THEN 'Excellent' WHEN score = 'B' THEN 'Very Good' WHEN score = 'C' THEN 'Good' WHEN score = 'D' THEN 'Fair' WHEN score = 'F' THEN 'Poor' ELSE 'No such score' END; RAISE NOTICE 'score = %, comments = %', score, comments; 54 第 3 章 PL/SQL 语言基础 END; / 结果: NOTICE: score = , comments = No score assigned 3.7.8 静态表达式 静态表达式的值可以在编译时确定,也就是说,它不包括字符比较、变量或函数调用。静态表达式的定义: • 如果表达式是 NULL 常数值,则它是静态的。 • 如果表达式是字符、数字或布尔常数,则它是静态的。 • 如果表达式是对静态常数的引用,则是静态的。 • 如果表达式是静态表达式中允许的运算符,如果其所有操作数都是静态的,并且在对这些操作数求值时,运算 符未引发异常,则表达式是静态的。 运算符 运算符类别 () 表达式分隔符 +,-,*,/ 算术运算符(加、减、乘、除) =, !=, <, <=, >=, > IS [NOT] NULL 比较运算符 NOT 逻辑运算符 [NOT] LIKE, [NOT] LIKE2, [NOT] LIKE4, [NOT] LIKEC 模式匹配运算符 3.7.8.1 PLS_INTEGER 静态表达式 PLS_INTEGER 静态表达式是: • PLS_INTEGER 常数 • PLS_INTEGER 静态常量 • NULL 3.7.8.2 BOOLEAN 静态表达式 BOOLEAN 静态表达式是: 55 第 3 章 PL/SQL 语言基础 • BOOLEAN 常数(TRUE,FALSE 或 NULL) • BOOLEAN 静态常量 • x 和 y 是 PLS_INTEGER 类型的静态表达式: –x > y –x < y –x >= y –x <= y –x = y –x <> y • x 和 y 是 BOOLEAN 类型的表达式: –NOT y –x AND y –x OR y –x > y –x >= y –x = y –x <= y –x <> y • x 是静态表达式: –x IS NULL –x IS NOT NULL 3.7.8.3 VARCHAR2 静态表达式 VARCHAR2 静态表达式是: • 最大为 32767 字节的字符串常数 • NULL • TO_CHAR(x),其中 x 是 PLS_INTEGER 类型静态表达式 • x||y,其中 x 和 y 是 VARCHAR2 或 PLS_INTEGER 类型静态表达式 56 第 3 章 PL/SQL 语言基础 3.7.8.4 静态常量 在包规范中使用以下语法声明静态常量: constant_name CONSTANT data_type := static_expression; 其中,static_expression 的类型必须与 data_type 相同(BOOLEAN 或 PLS_INTEGER)。 静态常量也必须以 package_name.constant_name 形式被引用,即便在 package_name 包体内。 3.8 错误报告函数 PL/SQL 有两个错误报告函数 SQLCODE 和 SQLERRM,用于 PL/SQL 异常处理代码。 不能在 SQL 语句中使用 SQLCODE 和 SQLERRM 函数。 另请参阅: • “SQLCODE 函数” • “SQLERRM 函数” 57 第4章 数据类型 4 第 章 数据类型 每个 PL/SQL 常量、变量、参数和函数返回值都有一个数据类型,它决定了它的存储格式以及它的有效值和操 作。 本章解释了标量数据类型,它存储没有内部组件的值。 标量数据类型可以有子类型。子类型是一种数据类型,它是另一种数据类型的子集,另一种数据类型是它的基本 类型。子类型具有与其基类型相同的有效操作。数据类型及其子类型构成数据类型簇。 PL/SQL 预定义了许多类型和子类型,并允许您定义自己的子类型。 PL/SQL 标量数据类型有: • SQL 数据类型 • PLS_INTEGER 等 subtype • REF CURSOR • 用户定义的子类型 4.1 SQL 数据类型 PL/SQL 数据类型包括 SQL 数据类型。 有关 SQL 数据类型的信息,请参阅《KingbaseES SQL 语言参考手册》其中有关数据类型和子类型、数据类型 比较规则、数据类型转换规则、字符和格式模型的所有信息都适用于 SQL 和 PL/SQL,除非此处另有说明: • 新增的 BINARY_FLOAT 和 BINARY_DOUBLE 的 PL/SQL 子类型 与 SQL 不同,PL/SQL 允许声明以下类型的变量: • CHAR 和 VARCHAR2 变量 4.1.1 BINARY_FLOAT 和 BINARY_DOUBLE 的其他 PL/SQL 子类型 PL/SQL 预定义了这些子类型: 58 第4章 数据类型 • SIMPLE_FLOAT,SQL 数据类型 BINARY_FLOAT 的子类型 • SIMPLE_DOUBLE,SQL 数据类型 BINARY_DOUBLE 的子类型 每个子类型都具有与其基本类型相同的范围,并且具有 NOT NULL 约束。 如果您知道变量永远不会有 NULL 值,请将其声明为 SIMPLE_FLOAT 或 SIMPLE_DOUBLE,而不是 BINARY_FLOAT 或 BINARY_DOUBLE。在没有检查空值的情况下,子类型提供了比它们的基本类型更好的性能。 4.1.2 CHAR 和 VARCHAR2 变量 分配或插入过长的值 4.1.2.1 如果分配给字符变量的值长于变量的最大长度,则会发生错误。例如: \set SQLTERM / DECLARE c VARCHAR2(4 CHAR); BEGIN c := 'hello'; END; / 结果: ERROR: value too long for type character varying(4) CONTEXT: PL/SQL function inline_code_block line 4 at assignment 同样,如果将字符变量插入到列中,并且变量的值长于定义的列宽度,则会发生错误。例如: CREATE TABLE t1 (c CHAR(4 CHAR)); \set SQLTERM / DECLARE str VARCHAR2(5 CHAR) := 'hello'; BEGIN INSERT INTO t1(c) VALUES(str); END; / 结果: ERROR: value too long for column "public"."t1"."c" (actual:5, maximum:4) CONTEXT: SQL statement "INSERT INTO t1(c) VALUES(str)" PL/SQL function inline_code_block line 4 at SQL statement 59 第4章 数据类型 要在将字符值分配给变量或将其插入列之前从字符值中会去除空格,请使用《KingbaseES SQL 语言参考手册》 TRIM 中解释的函数。例如: \set SQLTERM / DECLARE c VARCHAR2(3 CHAR); BEGIN c := TRIM(' qwe '); INSERT INTO t1(c) VALUES(TRIM(' qwe ')); END; / SELECT * FROM t1; / 结果: c -----qwe (1 row) 4.1.2.2 为多字节字符声明变量 CHAR 或 VARCHAR2 变量的最大长度为 32,767 字节,无论您以字符还是字节为单位指定最大长度。变量中的 最大字符数取决于字符集类型,有时还取决于字符本身: 字符集类型 最大字符数 单字节字符集 32,767 n 字节固定宽度多字节字符集 FLOOR(32,767/n) n -byte 可变宽度多字节字符集,字符 取决于字符本身——可以是从 32,767(对于仅包含 1 字节字符的字符串) 宽度介于 1 和 n 字节之间 到(对于仅包含 n 字节字符的字符串)的任何值。 声明 CHAR 或 VARCHAR2 变量时,为确保它始终可以容纳任何多字节字符集中的 n 个字符,请以字符形式声 明其长度———即 CHAR(n CHAR) 或 VARCHAR2(n CHAR),其中 n 不超过 FLOOR (32767/4) = 8191。 4.1.2.3 CHAR 和 VARCHAR2 数据类型的区别 CHAR 和 VARCHAR2 数据类型的不同之处在于: • 预定义的子类型 • 空白填充的工作原理 60 第4章 数据类型 • 值的比较 4.1.2.3.1 预定义子类型 CHAR 数据类型在 PL/SQL 和 SQL 中都有一个预定义的子类型——CHARACTER。 VARCHAR2 数据类型在 PL/SQL 和 SQL 中都有一个预定义的子类型 VARCHAR,在 PL/SQL 中还有一个预 定义的子类型 STRING。 每个子类型具有与其基本类型相同的值范围。 注意: 在未来的 PL/SQL 版本中,为了适应新兴的 SQL 标准,VARCHAR 可能成为一种单独的数据类型,不再是 VARCHAR2 的同义词。 4.1.2.3.2 空白填充的工作原理 这解释了在 CHAR 和 VARCHAR2 中使用空白填充的区别和注意事项。 考虑以下情况: • 您分配给变量的值小于变量的最大值。 • 您插入到列中的值比定义的列宽度短。 • 您从列检索到变量中的值小于变量的最大值。 如果接收者的数据类型是 CHAR,PL/SQL 将值填充到最大长度。有关原始值中尾随空格的信息会丢失。 如果接收者的数据类型是 VARCHAR2,PL/SQL 既不填充值也不去除尾随空格。字符值被原封不动地分配,不 会丢失任何信息。 示例 4-1 CHAR 和 VARCHAR2 空白填充差异 在此示例中,CHAR 变量和 VARCHAR2 变量的最大长度为 10 个字符。每个变量都接收一个由五个字符组成的 值,后面有一个空格。 分配给 CHAR 变量的值被空白填充到 10 个字符,并且您无法判断结果值中的六个尾随空白之一在原始值中。 分配给 VARCHAR2 变量的值没有改变,您可以看到它有一个尾随空格。 \set SQLTERM / DECLARE chr CHAR(10 CHAR); varchr VARCHAR2(10 CHAR); BEGIN chr := 'test '; varchr := 'test '; raise notice 'chr = (%)',chr; 61 第4章 数据类型 raise notice 'varchr = (%)',varchr; END; / 结果: NOTICE: chr = (test NOTICE: varchr = (test ) ) 4.1.2.3.3 值比较 比较字符值的 SQL 规则适用于 PL/SQL 字符变量。 每当比较中的一个或两个值具有数据类型 VARCHAR2 或 NVARCHAR2 时,应用非填充比较语义;否则,将应 用空白填充语义。有关详细信息,请参阅《KingbaseES SQL 语言参考手册》。 4.2 布尔数据类型 PL/SQL 数据类型 BOOLEAN 存储逻辑值,即布尔值 TRUE 和 FALSE 以及 NULL 值。NULL 表示未知值。 BOOLEAN 声明变量的语法是: variable_name BOOLEAN 您可以分配给 BOOLEAN 变量的唯一值是 BOOLEAN 表达式。有关详细信息,请参阅“布尔表达式”。 您不能将 BOOLEAN 值传递给 DBMS_OUTPUT.PUT 或 DBMS_OUTPUT.PUTLINE 子程序。要打印 BOOLEAN 值,请使用 IF 或 CASE 语句将其转换为字符值(有关这些语句的信息,请参阅“条件选择语句”)。 示例 4-2 打印 BOOLEAN 值 在此示例中,过程接受 BOOLEAN 参数并使用 CASE 语句打印 Unknown 如果参数的值为 NULL,如果为 TRUE,则打印 Yes,如果为 FALSE,则打印 No。 \set SQLTERM / CREATE OR REPLACE PROCEDURE show_boolean (a BOOLEAN) AS BEGIN RAISE NOTICE '%',CASE WHEN a IS NULL THEN 'Unknown' WHEN a THEN 'Yes' WHEN NOT a THEN 'No' END; END; / BEGIN 62 第4章 数据类型 show_boolean(TRUE); show_boolean(FALSE); show_boolean(NULL); END; / 结果: NOTICE: Yes NOTICE: No NOTICE: Unknown 示例 4-3 SQL 语句使用 BOOLEAN 参数调用 PL/SQL 函数 在这个例子中,一条 SQL 语句调用一个带有 BOOLEAN 参数的 PL/SQL 函数。 drop table if exists student; create table student (id int PRIMARY KEY, name text, score number); insert into student values (1,'xx',99); insert into student values (2,'xm',80); \set SQLTERM / CREATE OR REPLACE FUNCTION f1 (a BOOLEAN, b PLS_INTEGER) RETURN student.id%TYPE AUTHID CURRENT_USER AS BEGIN IF a THEN RETURN b; ELSE RETURN 2*b; END IF; END; / DECLARE name student.name%TYPE; b BOOLEAN := TRUE; BEGIN SELECT name INTO name FROM student WHERE id = f1(b, 1); raise notice 'name = %',name; b := FALSE; SELECT name INTO name FROM student 63 第4章 数据类型 WHERE id = f1(b, 1); raise notice 'name = %',name; END; / 结果: NOTICE: name = xx NOTICE: name = xm 4.3 PLS_INTEGER 和 BINARY_INTEGER 数据类 型 PL/SQL 数据类型 PLS_INTEGER 和 BINARY_INTEGER 是相同的。 为简单起见,本文档使用 PLS_INTEGER 来表示 PLS_INTEGER 和 BINARY_INTEGER。 PLS_INTEGER 数据类型存储 -2,147,483,648 到 2,147,483,647 范围内的带符号整数,以 32 位表示。 4.3.1 防止 PLS_INTEGER 溢出 具有溢出 PLS_INTEGER 范围的两个 PLS_INTEGER 值的计算会引发溢出异常。 对于 PLS_INTEGER 范围之外的计算,请使用 INTEGER,这是 NUMBER 数据类型的预定义子类型。 示例 4-4 PLS_INTEGER 计算引发溢出异常 此示例显示具有溢出 PLS_INTEGER 范围的两个 PLS_INTEGER 值的计算会引发溢出异常,即使您将结果分 配给 NUMBER 数据类型也是如此。 \set SQLTERM / DECLARE a1 PLS_INTEGER := 2147483647; a2 PLS_INTEGER := 1; num NUMBER; BEGIN num := a1 + a2; END; / 结果: 64 第4章 ERROR: 数据类型 integer out of range CONTEXT: PL/SQL function inline_code_block line 6 at assignment 4.3.2 预定义的 PLS_INTEGER 子类型 此摘要列出了 PLS_INTEGER 数据类型的预定义子类型并描述了它们存储的数据。 表 4.3.1: PLS_INTEGER 数据类型的预定义子类型 数据类型 资料说明 NATURAL PLS_INTEGER 非负值 NATURALN PLS_INTEGER 带 NOT NULL 约束的非负值 POSITIVE 值为正值 PLS_INTEGER POSITIVEN PLS_INTEGER 带 NOT NULL 约束的正值 SIGNTYPE PLS_INTEGER 值 -1、0 或 1(用于编程三态逻辑) SIMPLE_INTEGER PLS_INTEGER 有 NOT NULL 约束的值。 PLS_INTEGER 它的子类型可以隐式转换为这些数据类型: • CHAR • VARCHAR2 • NUMBER 除所有 PLS_INTEGER 子类型外,上述所有数据类型都可以隐式转换为 PLS_INTEGER。 仅当 PLS_INTEGER 值不违反子类型的约束时,才能将其隐式转换为 PLS_INTEGER 子类型。 示例 4-5 违反 SIMPLE_INTEGER 子类型的约束 此示例显示将 PLS_INTEGER NULL 值转换为 SIMPLE_INTEGER 子类型会引发异常。 \set SQLTERM / DECLARE x SIMPLE_INTEGER := 1; y PLS_INTEGER := NULL; BEGIN x := y; END; / 结果: 65 第4章 ERROR: 数据类型 domain simple_integer does not allow null values CONTEXT: PL/SQL function inline_code_block line 5 at assignment 4.3.3 PLS_INTEGER 的 SIMPLE_INTEGER 子类型 SIMPLE_INTEGER 是 PLS_INTEGER 数据类型的预定义子类型。 SIMPLE_INTEGER 与 PLS_INTEGER 具有相同的范围并具有 NOT NULL 约束。它在溢出语义上与 PLS_INTEGER 有很大不同。 如果您知道变量永远不会有 NULL 值 或 需 要 溢 出 检 查, 请 将 其 声 明 为 SIMPLE_INTEGER 而不是 PLS_INTEGER。在没有检查空值和溢出的情况下,SIMPLE_INTEGER 的性能明显优于 PLS_INTEGER。 4.4 用户定义的 PL/SQL 子类型 PL/SQL 允许您定义自己的子类型。 基本类型可以是任何标量或用户定义的 PL/SQL 数据类型说明符,例如 CHAR、DATE 或 RECORD(包括先 前定义的用户定义子类型)。 注意: 本主题中的信息适用于用户定义的子类型和 PL/SQL 预定义类型中列出的预定义子类型。 子类型可以: • 显示该类型数据项的预期用途 • 检测超出范围的值 4.4.1 无约束子类型 不受约束的子类型与其基类型具有相同的值集,因此它只是基类型的另一个名称。 因此,相同基本类型的无约束子类型可以相互互换,也可以与基本类型互换。不发生数据类型转换。 要定义不受约束的子类型,请使用以下语法: SUBTYPE subtype_name IS base_type 有关 subtype 的信息 base_type,请参阅E PL/SQL 预定义类型 。 PL/SQL 预定义以与 ANSI 兼容的无约束子类型的示例是: 66 第4章 数据类型 SUBTYPE "DOUBLE PRECISION" IS FLOAT 示例 4-6 用户定义的无约束子类型预期用途展示 在此示例中,不受约束的子类型 num_subtype 和 natural_subtype 显示了其数据类型的预期用途。 \set SQLTERM / DECLARE SUBTYPE num_subtype IS NUMBER; num1 num_subtype(6,2); SUBTYPE natural_subtype IS NATURAL; nat1 natural_subtype := 1; PROCEDURE add_num ( num1 IN OUT num_subtype, num2 IN num_subtype ) IS BEGIN num1 := num1 + num2; nat1 := nat1 + 1; END; BEGIN NULL; END; / 4.4.2 约束子类型 受约束的子类型仅具有其基本类型的值的子集。 如果基本类型允许您指定大小、精度和比例或值的范围,那么您可以为其子类型指定它们。子类型定义语法是: SUBTYPE subtype_name IS base_type { precision [, scale ] | RANGE low_value .. high_value } [ NOT NULL ] 否则,您可以对其子类型施加的唯一约束是 NOT NULL: SUBTYPE subtype_name IS base_type [ NOT NULL ] 注意: 您可以为其指定值范围的唯一基本类型是 PLS_INTEGER 及其子类型(预定义的和用户定义的)。 67 第4章 数据类型 受约束的子类型可以隐式转换为其基类型,但只有当值不违反子类型的约束时,才能将基类型隐式转换为受约束 的子类型。 仅当源值不违反目标子类型的约束时,才能将受约束的子类型隐式转换为具有相同基类型的另一个受约束的子类 型。 示例 4-7 用户定义的约束子类型被检测超出数值范围 在此示例中,num2 作为受约束的子类型 num_subtype,被检测超出了数值范围。 \set SQLTERM / DECLARE SUBTYPE num_subtype IS NUMBER(8,2); num1 num_subtype; num2 num_subtype; BEGIN num1 := 1000.00; num2 := 2000000.00; END; / 结果: ERROR: numeric field overflow DETAIL: A field with precision 8, scale 2 must round to an absolute value less than 10^6. CONTEXT: PL/SQL function inline_code_block line 7 at assignment 示例 4-8 具有相同基本类型的约束子类型之间的隐式转换 在此示例中,三个受约束的子类型具有相同的基本类型。前两个子类型可以隐式转换为第三个子类型,但不能相 互转换。 \set SQLTERM / DECLARE SUBTYPE typ1 IS PLS_INTEGER RANGE 0..9; SUBTYPE typ2 IS PLS_INTEGER RANGE 10..99; SUBTYPE typ3 IS PLS_INTEGER RANGE 0..99; val1 typ1 := 4; val2 typ2 := 35; val3 typ3; BEGIN val3 := val1; --true val1 := val2; --error val2 := val1; --error END; / 结果: 68 第4章 ERROR: 数据类型 numeric or value error CONTEXT: PL/SQL function inline_code_block line 10 at assignment 4.4.3 具有相同数据类型簇的基类型的子类型 如果两个子类型在同一数据类型簇中具有不同的基类型,则只有在源值不违反目标子类型的约束时,才能将一个 子类型隐式转换为另一种。 对于按数据类型簇分组的预定义 PL/SQL 数据类型和子类型,请参阅“PL/SQL 预定义类型”。 示例 4-9 具有相同系列的基本类型的子类型之间的隐式转换 在此示例中,子类型 chr6 和 varchr15 在同一数据类型系列中具有不同的基本类型。第一个赋值语句将 chr6 值 隐式转换为 varchr15。第二个赋值语句将 varchr15 值隐式转换为 chr6 第三个赋值语句无法将 varchr15 值隐式转换为 chr6,因为该值太长。 \set SQLTERM / DECLARE SUBTYPE chr6 IS CHAR(6); SUBTYPE varchr15 IS VARCHAR2(15); word chr6 := 'hei'; val1 varchr15; val2 varchr15 := 'test'; val3 varchr15 := 'test_test_test'; BEGIN val1 := word; --true word := val2; --true word := val3; --error END; / 结果: ERROR: value too long for type character(6) CONTEXT: PL/SQL function inline_code_block line 13 at assignment 69 第5章 控制语句 5 第 章 控制语句 PL/SQL 有三类控制语句:条件选择语句、循环语句和顺序控制语句。 PL/SQL 控制语句的类别有: • 条件选择语句,针对不同的数据值运行不同的语句。 条件选择语句包括 IF 语句和 CASE 语句。 • 循环语句,它使用一系列不同的数据值运行相同的语句。 循环语句包括基本的 LOOP 语句、FOR LOOP 语句和 WHILE LOOP 语句。 EXIT 语句将控制转移到循环结束。CONTINUE 语句退出循环的当前迭代并将控制转移到下一个迭代。EXIT 和 CONTINUE 都有一个可选的 WHEN 子句,您可以在其中指定一个条件。 • 顺序控制语句,这对于 PL/SQL 编程并不重要。 顺序控制语句包括转到指定语句的 GOTO 语句和不执行任何操作的 NULL 语句。 5.1 条件选择语句 条件选择语句 IF 和 CASE 针对不同的数据值运行不同的语句。 IF 语句根据条件运行或跳过一个或多个语句。IF 语句有以下形式: • IF THEN • IF THEN ELSE • IF THEN ELSIF CASE 语句从一系列条件中进行选择,并运行相应的语句。CASE 语句有以下形式: • 简单 CASE 语句,它计算单个表达式并将其与几个值进行比较。 • 搜索 CASE 语句,它计算多个条件并选择第一个为真的条件。 当要对每个备选方案采取不同的操作时,CASE 语句更适用。 70 第5章 控制语句 5.1.1 IF THEN 语句 IF THEN 语句根据条件运行或跳过一个或多个语句。 IF THEN 语句具有以下结构: IF condition THEN statements END IF; 如果 condition 为真,则 statements 运行;否则,statements 不运行。 有关完整的语法,请参阅“IF 语句”。 示例 5-1 IF THEN 语句 在此示例中,当且仅当 a 大于 b 时,打印 a - b 的值。 \set SQLTERM / DECLARE PROCEDURE p(a int, b int) AS BEGIN IF a > b THEN RAISE NOTICE 'a - b = %', a - b; END IF; END; BEGIN p(1,2); p(3,2); END; / 结果: NOTICE: a - b = 1 5.1.2 IF THEN ELSE 语句 IF THEN ELSE 语句具有以下结构: IF condition THEN statements ELSE else_statements END IF; 71 第5章 控制语句 如果条件的值为真,则 statements 运行;否则,else_statements 运行。 IF 语句可以嵌套,如示例 5-3 所示。 有关完整的语法,请参阅“IF 语句”。 示例 5-2 IF THEN ELSE 语句 在这个例子中,当 a 大于 b 时,打印 a - b 的值,当 a 小于等于 b 时,打印 a + b 的值。 \set SQLTERM / DECLARE PROCEDURE p(a int, b int) AS BEGIN IF a > b THEN RAISE NOTICE 'a - b = %', a - b; ELSE RAISE NOTICE 'a + b = %', a + b; END IF; END; BEGIN p(1, 2); p(3, 2); END; / 结果: NOTICE: a + b = 3 NOTICE: a - b = 1 示例 5-3 嵌套的 IF THEN ELSE 语句 \set SQLTERM / DECLARE PROCEDURE p(a int, b int) AS BEGIN IF a > b THEN RAISE NOTICE 'a - b = %', a - b; ELSE IF a = b THEN RAISE NOTICE 'a = b'; ELSE RAISE NOTICE 'a + b = %', a + b; END IF; END IF; END; BEGIN 72 第5章 控制语句 p(1, 2); p(2, 2); p(3, 2); END; / 结果: NOTICE: a + b = 3 NOTICE: a = b NOTICE: a - b = 1 5.1.3 IF THEN ELSIF 语句 IF THEN ELSIF 语句具有以下结构: IF condition_1 THEN statements_1 ELSIF condition_2 THEN statements_2 [ ELSIF condition_3 THEN statements_3 ]... [ ELSE else_statements ] END IF; IF THEN ELSIF 语 句 运 行 条 件 为 真 的 第 一 条 语 句, 不 计 算 剩 余 条 件。 如 果 没 有 条 件 为 真, 则 运 行 else_statements(如果存在);否则,IF THEN ELSIF 语句什么也不执行。 单个 IF THEN ELSIF 语句比逻辑等效的嵌套 IF THEN ELSE 语句更容易理解: -- IF THEN ELSIF statement IF condition_1 THEN statements_1; ELSIF condition_2 THEN statements_2; ELSIF condition_3 THEN statement_3; END IF; -- Logically equivalent nested IF THEN ELSE statements IF condition_1 THEN statements_1; 73 第5章 控制语句 ELSE IF condition_2 THEN statements_2; ELSE IF condition_3 THEN statements_3; END IF; END IF; END IF; 有关完整的语法,请参阅“IF 语句”。 示例 5-4 IF THEN ELSIF 语句 在本例中,使用 ELSIF 达到了和嵌套 IF THEN ELSE 同样的功能。 \set SQLTERM / DECLARE PROCEDURE p(a int, b int) AS BEGIN IF a > b THEN RAISE NOTICE 'a - b = %', a - b; ELSIF a = b THEN RAISE NOTICE 'a = b'; ELSE RAISE NOTICE 'a + b = %', a + b; END IF; END; BEGIN p(1, 2); p(2, 2); p(3, 2); END; / 结果: NOTICE: a + b = 3 NOTICE: a = b NOTICE: a - b = 1 示例 5-5 IF THEN ELSIF 语句模拟简单的 CASE 语句 此示例使用带有许多 ELSIF 子句的 IF THEN ELSIF 语句将单个值与许多可能值进行比较。相对比而言,一个 简单的 CASE 语句更清晰——见示例 5-6 。 74 第5章 控制语句 \set SQLTERM / DECLARE grade CHAR(1); BEGIN grade := 'A'; IF grade = 'A' THEN RAISE NOTICE 'Excellent'; ELSIF grade = 'B' THEN RAISE NOTICE 'Very Good'; ELSIF grade = 'C' THEN RAISE NOTICE 'Good'; ELSIF grade = 'D' THEN RAISE NOTICE 'Fair'; ELSIF grade = 'F' THEN RAISE NOTICE 'Poor'; ELSE RAISE NOTICE 'No such grade'; END IF; END; / 结果: NOTICE: Excellent 5.1.4 简单 CASE 语句 简单 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; selector 是一个表达式(通常是单个变量)。每个 selector_value 可以是文字或表达式。(对于完整的语法,请 参阅“CASE 语句”。) 简单 CASE 语句运行 selector_value 等于 selector 的第一条语句,不计算剩余条件。如果没有 selector_value 等于 selector,则 CASE 语句判断是否存在 ELSE ,如果存在则运行 else_statements ,否则引发预定义的异常 75 第5章 控制语句 CASE_NOT_FOUND。 示例 5-6 使用一个简单的 CASE 语句将单个值与多个可能值进行比较。示例 5-6 中的 CASE 语句在逻辑上等价 于示例 5-5 中的 IF THEN ELSIF 语句。 注意: 如果简单 CASE 语句中的 selector 的值为 NULL,则它不能被 WHEN NULL 匹配(参见示例 5-6 )。相反, 请使用带有 WHEN 条件 IS NULL 的搜索 CASE 语句(参见示例 5-8 )。 示例 5-6 简单的 CASE 语句 \set SQLTERM / DECLARE grade CHAR(1); BEGIN grade := 'B'; CASE grade WHEN 'A' THEN RAISE NOTICE 'Excellent'; WHEN 'B' THEN RAISE NOTICE 'Very Good'; WHEN 'C' THEN RAISE NOTICE 'Good'; WHEN 'D' THEN RAISE NOTICE 'Fair'; WHEN 'F' THEN RAISE NOTICE 'Poor'; ELSE RAISE NOTICE 'No such grade'; END CASE; END; / 结果: NOTICE: Very Good 5.1.5 搜索 CASE 语句 搜索 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; 76 第5章 控制语句 搜索 CASE 语句运行条件为真的第一条语句。不计算剩余条件。如果没有条件为真,则 CASE 语句判断是否存 在 ELSE ,如果存在则运行 else_statements ,否则引发预定义的异常 CASE_NOT_FOUND。(对于完整的语法, 请参阅“CASE 语句”。) 示例 5-7 中的搜索 CASE 语句在逻辑上等价于示例 5-6 中的简单 CASE 语句。 在示例 5-7 和示例 5-6 中,ELSE 子句都可以替换为 EXCEPTION。示例 5-8 在逻辑上等价于示例 5-7 。 示例 5-7 搜索 CASE 语句 DECLARE grade CHAR(1); BEGIN grade := 'B'; CASE WHEN grade = 'A' THEN RAISE NOTICE 'Excellent'; WHEN grade = 'B' THEN RAISE NOTICE 'Very Good'; WHEN grade = 'C' THEN RAISE NOTICE 'Good'; WHEN grade = 'D' THEN RAISE NOTICE 'Fair'; WHEN grade = 'F' THEN RAISE NOTICE 'Poor'; ELSE RAISE NOTICE 'No such grade'; END CASE; END; / 结果: NOTICE: Very Good 示例 5-8 CASE 语句中的 EXCEPTION 而不是 ELSE 子句 \set SQLTERM / DECLARE grade CHAR(1); BEGIN grade := 'B'; CASE WHEN grade = 'A' THEN RAISE NOTICE 'Excellent'; WHEN grade = 'B' THEN RAISE NOTICE 'Very Good'; WHEN grade = 'C' THEN RAISE NOTICE 'Good'; WHEN grade = 'D' THEN RAISE NOTICE 'Fair'; WHEN grade = 'F' THEN RAISE NOTICE 'Poor'; END CASE; EXCEPTION WHEN CASE_NOT_FOUND THEN 77 第5章 控制语句 RAISE NOTICE 'No such grade'; END; / 结果: NOTICE: Very Good 5.2 循环语句 循环语句使用一系列不同的值迭代地运行相同的语句。 一个 LOOP 语句包含三个部分: • 迭代变量 • 迭代器 • 循环执行体 loop_statement ::= [ iteration_scheme ] LOOP loop_body END LOOP [ label ]; iteration_scheme ::= WHILE expression | FOR iterator 循环语句有: • 基本的 LOOP • FOR LOOP • 游标 FOR LOOP • WHILE LOOP 退出循环的语句有: • EXIT • EXIT WHEN 退出当前循环迭代的语句有: • CONTINUE • CONTINUE WHEN EXIT、EXIT WHEN、CONTINUE 和 CONTINUE WHEN 可以出现在循环内的任何位置,但不能出现在循环 外。建议使用这些语句而不是 GOTO 语句,它可以通过将控制转移到循环外的语句来退出循环或循环的当前迭代。 78 第5章 控制语句 引发的异常也会退出循环。 LOOP 语句可以被标记,LOOP 语句可以嵌套。建议为嵌套循环使用标签以提高可读性。您必须确保 END LOOP 语句中的标签与同一循环语句开头的标签匹配,否则程序会报错。 另请参阅: • GOTO 语句 • CONTINUE 语句 • EXIT 语句 • 异常处理概述 了解有关异常的信息 • 使用游标 FOR LOOP 语句处理查询结果集 以获取有关游标 FOR LOOP 的信息 5.2.1 基本循环语句 LOOP 定义一个无条件的循环,随着循环的每次迭代,语句运行并且控制返回到循环的顶部。为了防止无限循 环,必须通过退出循环语句或引发异常来退出循环。 基本的 LOOP 语句具有以下结构: [ label ] LOOP statements END LOOP [ label ]; 另请参阅: • “基本 LOOP 语句” 5.2.2 FOR LOOP 语句概述 FOR LOOP 语句为循环索引的每个值运行一个或多个语句。 FOR LOOP 头指定迭代器。迭代器指定一个迭代和迭代控件。迭代控件向迭代对象提供一系列值,以便在循环 体中进行使用。循环体中有语句对迭代的每一个值进行执行。 可用的迭代控制是: • Stepped Range:一种迭代控件,可生成一系列步进数值。当未指定步长时,计数控制是步长为 1 的 integer 类 型的步长范围。 • 单个表达式:计算单个表达式的迭代控件。 • 重复表达式:重复计算单个表达式的迭代控件。 • 游标:一个迭代控件,它从游标、游标变量或动态 SQL 生成所有记录。 FOR LOOP 语句具有以下结构: 79 第5章 控制语句 [ label ] for_loop_header statements END LOOP [ label ]; for_loop_header ::= FOR iterator LOOP iterator ::= iterand_decl [, iterand_decl] IN iteration_ctl_seq iterand_decl ::= pls_identifier [ constrained_type ] iteration_ctl_seq ::= qual_iteration_ctl [,]... qual_iteration_ctl ::= [ REVERSE ] iteration_control iteration_control ::= stepped_control | single_expression_control | cursor_iteration_control stepped_control ::= lower_bound .. upper_bound [ BY step ] single_expression_control ::= expr cursor_iteration_control ::= ( cursor_object ) | ( cursor_variable ) | ( dynamic_sql ) | ( sql_statement ) 5.2.2.1 FOR LOOP 迭代 FOR LOOP 语句的索引或迭代数被隐式或显式声明为循环局部的变量。 循环中的语句可以读取迭代的值,但不能更改它。循环外的语句不能引用迭代。 示例 5-9 外部语句引用 FOR LOOP 语句索引 在此示例中,FOR LOOP 语句之外的语句引用了循环索引,从而导致错误。 \set SQLTERM / BEGIN FOR i IN 1..3 LOOP RAISE NOTICE 'Inside loop, i = %', i; END LOOP; RAISE NOTICE 'Outside loop, i = %', i; END; / 结果: NOTICE: Inside loop, i = 1 NOTICE: Inside loop, i = 2 NOTICE: Inside loop, i = 3 ERROR: column "i" does not exist LINE 1: SELECT i ^ QUERY: SELECT i CONTEXT: PL/SQL function inline_code_block line 5 at RAISE 80 第5章 控制语句 示例 5-10 与变量同名的 FOR LOOP 语句索引 如果 FOR LOOP 语句的索引与匿名块中声明的变量同名,则局部隐式声明会隐藏另一个声明,如本例所示。 \set SQLTERM / DECLARE i NUMBER := 10; BEGIN FOR i IN 1..5 LOOP RAISE NOTICE 'Inside loop, i = %', i; END LOOP; RAISE NOTICE 'Outside loop, i = %', i; END; / 结果: NOTICE: Inside loop, i = 1 NOTICE: Inside loop, i = 2 NOTICE: Inside loop, i = 3 NOTICE: Inside loop, i = 4 NOTICE: Inside loop, i = 5 NOTICE: Outside loop, i = 10 示例 5-11 FOR LOOP 语句引用与索引同名的变量 这个例子展示了如何更改例示例 5-10 以允许循环内的语句引用匿名块中声明的变量。 CREATE OR REPLACE PROCEDURE p AS <

> DECLARE i NUMBER := 10; BEGIN FOR i IN 1..5 LOOP RAISE NOTICE 'local: %, global: %',i ,main.i; END LOOP; END; / call p(); / 结果: NOTICE: local: 1, global: 10 NOTICE: local: 2, global: 10 81 第5章 NOTICE: local: 3, global: 10 NOTICE: local: 4, global: 10 NOTICE: local: 5, global: 10 控制语句 示例 5-12 具有相同索引名称的嵌套 FOR LOOP 语句 在此示例中,嵌套 FOR LOOP 语句的索引具有相同的名称。内循环通过使用外循环的标签来引用外循环的索 引。仅为了清楚起见,内部循环还使用自己的标签限定对自己索引的引用。 \set SQLTERM / BEGIN <> FOR i IN 1..3 LOOP <> FOR i IN 1..3 LOOP IF label1.i = 2 THEN RAISE NOTICE 'lable1: i = %, label2: i = %',label1.i, label2.i; END IF; END LOOP label2; END LOOP label1; END; / 结果: NOTICE: lable1: i = 2, label2: i = 1 NOTICE: lable1: i = 2, label2: i = 2 NOTICE: lable1: i = 2, label2: i = 3 5.2.2.2 步进范围迭代控制 步进范围迭代控件生成一系列数值。 控制表达式是下限、上限和步长。 stepped_control ::= [ REVERSE ] lower_bound..upper_bound [ BY step ] lower_bound ::= numeric_expression upper_bound ::= numeric_expression step ::= numeric_expression 在 PL/SQL 中使用步进范围迭代控制: 当迭代控制初始化时,每个控制表达式都被计算并转换为迭代的类型。Step 必须具有严格的正数值。如果在计 算控制表达式时发生任何异常,则放弃循环并进行正常的异常处理。未指定步长时,其值为 1。由步进范围迭代控制 生成的值逐步从下限到上限。当指定 REVERSE 时,值从上限逐步递减到下限。如果迭代具有浮点类型,则循环控 82 第5章 控制语句 制值的某些组合可能会因为舍入误差而创建无限循环。当迭代是可变的并且在循环体中被修改时,修改后的值将用于 下一次迭代更新中的增量和循环结束检测,这样可能会改变循环处理的值的顺序。 示例 5-13 FOR LOOP 语句范围迭代控制 在此示例中,迭代 i 的 lower_bound 为 1,upper_bound 为 3。循环打印从 1 到 3 的数字。 \set SQLTERM / BEGIN FOR i IN 1..3 LOOP RAISE NOTICE '%',i; END LOOP; END; / 结果: NOTICE: 1 NOTICE: 2 NOTICE: 3 示例 5-14 反向 FOR LOOP 语句范围迭代控制 此示例中的 FOR LOOP 语句打印从 3 到 1 的数字。循环变量 i 隐式声明为 PLS_INTEGER(计数和索引循环 的默认值)。 \set SQLTERM / BEGIN FOR i IN REVERSE 1..3 LOOP RAISE NOTICE '%',i; END LOOP; END; / 结果: NOTICE: 3 NOTICE: 2 NOTICE: 1 示例 5-15 步进范围迭代控件 此示例显示一个循环变量 i, 计数器的增量为 2。 \set SQLTERM / BEGIN FOR i IN 1 .. 10 BY 2 LOOP RAISE NOTICE '%',i; 83 第5章 控制语句 END LOOP; END; / 结果: NOTICE: 1 NOTICE: 3 NOTICE: 5 NOTICE: 7 NOTICE: 9 示例 5-16 使用 FOR LOOP 步进范围迭代器的简单步进过滤器 您可以使用步进范围迭代器来实现步进过滤器。 FOR i IN lower..upper BY k LOOP newcol(i) := col(i) END LOOP; 5.2.2.3 游标迭代控制 游标迭代控件生成由显式或隐式游标返回的 RECORD 序列。 游标定义是控制表达式。不能将 REVERSE 与游标迭代控件一起使用。 cursor_iteration__control ::= { cursor_object| sql_statement| cursor_variable| dynamic_sql } cursor_object 是一个显式的 PL/SQL 游标对象。sql_statement 是为直接在迭代控件中指定的 SQL 语句创建的 隐式 PL/SQL 游标对象。 cursor_variable 是一个 PL/SQL REF CURSOR 对象。在循环体中修改迭代时,对迭代控件生成的下一个值没 有影响。当游标变量遇到迭代控件时必须打开,否则会引发异常。当迭代控制用尽时,它保持打开状态。如果在执行 循环体期间不是通过迭代访问游标变量,则行为未指定。 在 PL/SQL 中使用游标迭代控制: 计算游标以创建迭代向量。在向量中获取一个值来迭代创建下一个值。如果迭代向量为空,则无法进行迭代向量 创建。计算循环体,对获取的每个迭代值重复相同的操作,直到无法进行迭代控制。 示例 5-17 游标迭代控件 84 第5章 控制语句 此示例创建 id 到表 T1 中数据的映射。 \set SQLTERM ; create table t1(id int); insert into t1 values(1),(2); \set SQLTERM / declare cursor c is SELECT * FROM T1; begin FOR r IN c LOOP RAISE NOTICE 'r = %',r; END LOOP; end; / 结果: NOTICE: r = (1) NOTICE: r = (2) ANONYMOUS BLOCK 在迭代控制中使用动态 SQL 5.2.2.4 ... dynamic_sql ::= EXECUTE dynamic_sql_stmt [ using_clause ] using_clause ::= USING [ [ IN ] (bind_argument [,])+ ] 动态 SQL 可用于游标迭代控制。如果它被用作第一个迭代控件,则必须为迭代指定显式类型(或为一对控件的 值迭代指定)。using_clause 是唯一允许的子句。不得使用 INTO 或动态返回子句。如果指定的 SQL 语句是一种不 能返回任何行的类型,则会报告运行时错误,类似于在普通 SQL 语句指定 bulk collect into 或 into 子句时报告的运 行时错误。 示例 5-18 使用动态 SQL 作为迭代控制 此示例显示迭代控件从动态 SQL 生成所有记录。它打印 id 小于 103 的所有员工的 name 和 id。 \set SQLTERM / DECLARE cursor_str VARCHAR2(500) := 'SELECT name, id FROM student where id = :1 ORDER BY name'; r record; i int := 1; BEGIN FOR r IN EXECUTE cursor_str USING i LOOP raise notice 'r = %',r; 85 第5章 控制语句 END LOOP; END; / 结果: NOTICE: r = (xx,1) 5.2.3 WHILE LOOP 语句 WHILE LOOP 语句在条件为真时运行一个或多个语句。 它有这样的结构: [ label ] WHILE condition LOOP statements END LOOP [ label ]; 如果条件为真,则语句运行并且控制返回到循环的顶部,再次计算条件。如果条件不成立,则控制转移到 WHILE LOOP 语句之后的语句。为了防止无限循环,循环内的语句必须使条件为 false 或 null。有关完整的语法, 请参阅“WHILE LOOP 语句”。 语句中的 EXIT、EXIT WHEN、CONTINUE 或 CONTINUE WHEN 会导致循环或循环的当前迭代提前结束。 某些语言具有 LOOP UNTIL 或 REPEAT UNTIL 结构,它在循环底部而不是顶部测试条件,以便语句至少运行 一次。要在 PL/SQL 中模拟这种结构,请使用带有 EXIT WHEN 语句的基本 LOOP 语句: LOOP statements EXIT WHEN condition; END LOOP; 5.2.4 FOREACH 语句 FOREACH 循环很像一个 FOR 循环,但不是通过一个 SQL 查询返回的行进行迭代,它通过数组值的元素来迭 代。 通常,FOREACH 意味着通过一个组合值表达式的部件迭代。 它有这样的结构: [ <
相关文章