总的目录和进度,请参见开始读 Oracle PL/SQL Programming 第6版
几乎您编写的每个 PL/SQL 块都会定义和操作程序数据。 程序数据由仅存在于 PL/SQL 会话中的数据结构组成(物理上,在会话的程序全局区域或 PGA 中); 它们不存储在数据库中。 程序数据可以是:
-
变量或常数
变量的值在程序执行期间可能会发生变化。 一旦在声明时设置,常量的值就是静态的。 -
标量或复合
标量由单个值组成,例如数字或字符串。 复合数据由多个值组成,例如记录、集合或对象类型实例。 -
容器化
容器可能包含从数据库获取的信息,或者数据库中从未存在且可能永远不会出现的数据。
在使用 PL/SQL 代码中的程序数据之前,必须声明数据结构,并为其指定名称和数据类型。
本章介绍如何声明程序数据。 它涵盖了管理数据结构名称格式的规则。 它提供了 PL/SQL 支持的所有不同类型数据的快速参考,并探讨了数据类型转换的概念。 本章最后给出了一些处理程序数据的建议。 本书这一部分的其余章节描述了程序数据的特定类型。
Naming Your Program Data
要使用变量或常量,您必须首先声明并给它一个名称。
名称必须以字母开头,最长 30 个字符。名称不区分大小写(除非放在双引号内,但不建议使用)。
以下是命名变量、常量和类型的两个重要建议:
-
确保每个名称准确反映其用法并且一目了然
-
建立一致、合理的命名约定
此类约定通常涉及使用前缀和/或后缀来指示类型和用法。 例如,局部变量应以“l_”为前缀,而包中定义的全局变量应以“g_”为前缀。 所有记录类型都应具有后缀“_rt”,依此类推。 作者的书《Oracle PL/SQL 最佳实践》中的示例代码提供了一套全面的命名约定可供下载。
Overview of PL/SQL Datatypes
声明变量或常量时,必须为其分配数据类型。 总体而言,PL/SQL 是一种“静态类型编程语言”(类型检查是在编译时而不是运行时执行,如C语言;动态类型编程语言包括Perl,JavaScript,Ruby)。静态类型可以在编译时发现类型错误,这可以增加程序的可靠性并使其执行速度更快。动态类型的好处是更容易实现metaclasses 和introspection。
PL/SQL 在STANDARD 包中提供了一套全面的预定义标量和复合数据类型,您可以创建用户定义类型(也称为抽象数据类型)。 一些类型如 Boolean 和 NATURAL并不能为数据库列定义所用。
Character Data
定长字符串(CHAR 和 NCHAR)和变长字符串(VARCHAR2 和 NVARCHAR2)类型。
字符大对象(CLOB和NCLOB)。LONG不建议用,只是为了向后兼容。
Numbers
NUMBER类型最常用,可用于十进制定点和浮点值以及整数。
二进制浮点类型:BINARY_FLOAT 和 BINARY_DOUBLE。具有二进制精度,不是十进制,因此可能有舍入,因此不能用于钱的表示。
SIMPLE_FLOAT and SIMPLE_DOUBLE类似于以上两类型,但它们不允许 NULL 值,也不会在发生溢出时引发异常。
PLS_INTEGER 是一种整数类型,其运算在硬件中实现。 FOR 循环计数器被实现为 PLS_INTEGER。 SIMPLE_INTEGER 与 PLS_INTEGER 具有相同的值范围,但它不允许 NULL 值,发生溢出时也不会引发异常。
Dates, Timestamps, and Intervals
DATE(日期和时间,时间精确到秒),INTERVAL和TIMESTAMP。
Booleans
BOOLEAN,三值逻辑,取值为 TRUE, FALSE和NULL之一。
布尔数据库类型直到23c才支持。之前可以用约束实现:
flag char(1) check (flag in ( 'Y', 'N' )),
Binary Data
RAW(无字符集转换), BLOB和BFILE(存储在数据库外),用于非结构化数据。
ROWID
ROWID 和 UROWID,用于表示表中行的地址。 ROWID 表示行在其表中的唯一物理地址; UROWID 表示索引组织表 (IOT) 中行的逻辑位置。 ROWID也是一个SQL伪列,可以包含在SQL语句中。
REF CURSORs
REF CURSOR 数据类型声明游标变量。 游标变量可以与静态或动态 SQL 语句一起使用,以实现更灵活的程序。 REF CURSOR 有两种形式:强 REF CURSOR 和弱 REF CURSOR。 PL/SQL 是一种静态类型语言,弱 REF CURSOR 是少数受支持的动态类型结构之一。
-- 强 REF CURSORTYPE book_data_t IS REF CURSOR RETURN book%ROWTYPE;
-- 弱 REF CURSOR TYPE book_data_t IS REF CURSOR;book_curs_var_b SYS_REFCURSOR;
Internet Datatypes
XMLType,URIType 和 HttpURIType。
“Any” Datatypes
Any 数据类型主要用于编写更通用的代码,允许您动态封装和访问任何其他 SQL 类型的类型描述、数据实例和数据实例集。 您可以使用这些类型(以及为它们定义的方法,因为它们是对象类型)来执行诸如确定存储在特定嵌套表中的数据类型之类的操作,而无需访问该表类型的实际声明!
Any 数据类型包括 AnyType、AnyData 和 AnyDataSet。
User-Defined Datatypes
您可以使用 Oracle 内置数据类型和其他用户定义的数据类型来创建您自己的任意复杂类型,以模拟系统中数据的结构和行为。
Declaring Program Data
除了少数例外,您必须在使用变量和常量之前声明它们。 这些声明位于 PL/SQL 程序的声明部分。 声明可以包括变量、常量、类型(例如集合类型或记录类型)和异常。
Declaring a Variable
当您声明变量时,PL/SQL 会为变量的值分配内存并命名存储位置,以便可以检索和更改该值。 声明还指定了变量的数据类型; 然后使用该数据类型来验证分配给变量的值。语法为:
name datatype [NOT NULL] [ := | DEFAULT default_assignment];
示例:
-- 错误,非空的变量和常数都必须赋值d1 NUMBER NOT NULL;d1 CONSTANT NUMBER;
-- 正确d1 NUMBER NOT NULL DEFAULT 5;
-- 正确,等同于上一句d2 NUMBER NOT NULL := 5;
作者建议对于常数使用赋值(:=),对于变量使用DEFAULT。这样代码易理解。
例如:
d1 CONSTANT NUMBER := 5;
d2 NUMBER DEFAULT 5;
Declaring Constants
在数据类型前加CONSTANT关键字即可,后续不允许改。
未命名常量是literal,例如 2 或“Bobby McGee”。 literal虽然没有名称,但它确实有隐含的(未声明的)数据类型。
The NOT NULL Clause
下面这个例子不错,会报错:
declared1 NUMBER NOT NULL := 5;
begind1 := NULL;
end;
/
Anchored Declarations
明确的指定数据类型,称为硬编码数据类型;锚定数据类型指将变量的数据类型设置为已定义的数据结构的数据类型:另一个 PL/SQL 变量、预定义的 TYPE 或 SUBTYPE、数据库表或数据库中表的特定列。
锚定又分为标量锚定和记录锚定:
-- 硬编码数据类型
a varchar2(100);
-- 锚定数据类型之标量锚定
b a%TYPE;
b employees.emp_id%TYPE;
锚定引用在编译代码时解析。如果这些元素发生更改,则发生锚定的代码将被标记为无效。
锚定声明很好地说明了:PL/SQL 不仅仅是一种过程式编程语言,而且是专门为 Oracle SQL 语言的扩展而设计的。
Anchoring to Cursors and Tables
-- 锚定数据类型之记录锚定
a employees%ROWTYPE;-- 显式记录锚定
CURSOR emp_cur ISSELECT emp_id, emp_name FROM employeesWHERE emp_id = 100;
l_emp_rec emp_cur%ROWTYPE;-- 隐式记录锚定
for l_emp_rec in (SELECT emp_id, emp_name FROM employees WHERE emp_id = 100;)
...
Benefits of Anchored Declarations
锚定好处包括:
- 与数据库列同步(保持一致)
- 局部变量定义的范式化(但业务需求变化时,可以很容易的一次性改动)
Anchoring to NOT NULL Datatypes
使用锚定时,数据库表中的 NOT NULL 约束不会自动传输到变量。而NOT NULL 变量则会传输。
Programmer-Defined Subtypes
也称为抽象数据类型。有约束和非约束两类。
-- 约束
SUBTYPE POSITIVE IS BINARY_INTEGER RANGE 1 .. 2147483647;-- 非约束
SUBTYPE FLOAT IS NUMBER;
非约束相当于别名。注意,锚定到数据库列的子类型不会将 NOT NULL 约束传递给它定义的变量。 它也不传输原始声明中包含的默认值。
Conversion Between Datatypes
数据类型转换分为隐式和显式两种。
Implicit Data Conversion
建议隐式,可能会带来安全问题,代码可读性问题等。
隐式转换可能会产生异常,同时并非所有的类型都可以隐式转换。
隐式转换的坏处包括:
- PL/SQL 通常是一种静态类型语言。 当您的程序执行隐式转换时,您会失去静态类型的一些好处,例如代码的清晰度和安全性。
- 每个隐式转换都代表着您对程序的控制权的损失,您的代码可能受到影响。
- PL/SQL 执行的隐式转换取决于代码执行的上下文。 PL/SQL 执行的转换不一定是您所期望的转换。
- 显式转换使代码更易于阅读和理解,也可以消除潜在的误解。
可参考文章On Implicit Conversions and More,以及 How Can I Find The Implicit Conversion (Doc ID 423181.1)。
Explicit Datatype Conversion
Oracle 提供了一套全面的转换函数和运算符可在 SQL 和 PL/SQL 中使用,以下为主要的内置转换函数:
名字 | 描述 |
---|---|
ASCIISTR | 将任何字符集中的字符串转换为数据库字符集中的 ASCII 字符串 |
CAST | 将一种内置数据类型或集合类型值转换为另一种内置数据类型或集合类型值; 这种非常强大的转换机制可以用作 TO_DATE 等传统函数的替代品。 |
CHARTOROWID | 将字符串转换为 ROWID。 |
CONVERT | 将字符串从一种字符集转换为另一种字符集。 |
FROM_TZ | 将时区信息添加到 TIMESTAMP 值,从而将其转换为 TIMESTAMP WITH TIME ZONE 值。 |
HEXTORAW | 从十六进制转换为原始格式。 |
MULTISET | 将数据库表映射到集合。 |
NUMTODSINTERVAL | 将数字(或数值表达式)转换为 INTERVAL DAY TO SECOND 文字。 |
NUMTOYMINTERVAL | 将数字(或数值表达式)转换为 INTERVAL YEAR TO MONTH 文字。 |
RAWTOHEX, RAWTONHEX | 从原始值转换为十六进制。 |
REFTOHEX | 将 REF 值转换为包含 REF 值的十六进制表示形式的字符串。 |
ROWIDTOCHAR, ROWIDTONCHAR | 将二进制 ROWID 值转换为字符串。 |
TABLE | 将集合映射到数据库表; 这是 MULTISET 的逆过程。 |
THE | 将单行中的单列值映射到虚拟数据库表中。 |
TO_BINARY_DOUBLE | 将数字或字符串转换为 BINARY_DOUBLE。 |
TO_BINARY_FLOAT | 将数字或字符串转换为 BINARY_FLOAT。 |
TO_BLOB | 从 RAW 值转换为 BLOB。 |
TO_CHAR、TO_NCHAR(字符版本) | 在数据库字符集和国家字符集之间转换字符数据。 |
TO_CHAR、TO_NCHAR(日期版本) | 将日期转换为字符串。 |
TO_CHAR、TO_NCHAR(数字版本) | 将数字转换为字符串(分别为 VARCHAR2 或 NVARCHAR2)。 |
TO_CLOB、TO_NCLOB | 从 VARCHAR2、NVARCHAR2 或 NCLOB 值转换为 CLOB(或 NCLOB)。 |
TO_DATE | 将字符串转换为日期。 |
TO_DSINTERVAL | 将 CHAR、VARCHAR2、NCHAR 或 NVARCHAR2 数据类型的字符串转换为 INTERVAL DAY TO SECOND 类型。 |
TO_LOB | 从 LONG 转换为 LOB。 |
TO_MULTI_BYTE | 如果可能,将输入字符串中的单字节字符转换为其多字节等效项。 |
TO_NUMBER | 将字符串或数字(例如 BINARY_FLOAT)转换为 NUMBER。 |
TO_RAW | 从 BLOB 转换为 RAW。 |
TO_SINGLE_BYTE | 将输入字符串中的多字节字符转换为其相应的单字节字符。 |
TO_TIMESTAMP | 将字符串转换为 TIMESTAMP 类型的值。 |
TO_TIMESTAMP_TZ | 将字符串转换为 TO_TIMESTAMP_TZ 类型的值。 |
TO_YMINTERVAL | 将 CHAR、VARCHAR2、NCHAR 或 NVARCHAR2 数据类型的字符串转换为 INTERVAL YEAR TO MONTH 类型。 |
TRANSLATE … USING | 将提供的文本转换为为数据库字符集和国家字符集之间的转换指定的字符集。 |
UNISTR | 将任意字符集中的字符串作为参数,并以数据库 Unicode 字符集中的 Unicode 形式返回。 |
The CHARTOROWID function
输入字符串格式必须为OOOOOFFFBBBBBBRRR
。其中:
- OOOOO 是数据对象编号
- FFF 是数据库文件的相对文件编号
- BBBBBB 是文件中的块编号
- RRR 是块内的行编号
所有四个数字都必须采用 Base-64 格式。 如果格式不符,PL/SQL 将引发 VALUE_ERROR 异常。
The CAST function
例子:
-- 在SQL中的CAST
SELECT employee_id, cast (hire_date AS VARCHAR2 (30))FROM employee;-- PL/SQL中的CAST
hd_display := CAST (SYSDATE AS VARCHAR2);
详见Table 7-1 Casting Built-In Data Types
The CONVERT function
CONVERT 函数将字符串从一种字符集转换为另一种字符集。
The HEXTORAW function
HEXTORAW 函数将十六进制字符串从 CHAR 或 VARCHAR2 类型转换为 RAW 类型。
The RAWTOHEX function
RAWTOHEX 函数将值从 RAW 类型转换为 VARCHAR2 类型的十六进制字符串。
RAWTOHEX 始终返回可变长度字符串值。
The ROWIDTOCHAR function
ROWIDTOCHAR 函数将 ROWID 类型的二进制值转换为 VARCHAR2 类型的字符串。
该函数返回的字符串格式为OOOOOFFFBBBBBBRRR
。