Oracle-PL/SQL用户指南与参考-5
第三章 PL/SQL基础(续)
PL/SQL数据类型
一、预定义数据类型
Oracle 的数据类型可以分为四类,分别是 标量类型,复合类型,引用类型和LOB类型。标量类型没有内部组件;而复合类型包含了能够被单独操作的内部组件;引用类型类似于3G语言中的指针,能够引用一个值;LOB类型的值就是一个lob定位器,能够指示出大对象(如图像)的存储位置。
下图是在PL/SQL中可以使用的预定义类型
|
|
其中标量类型又分为四类:数字、字符、布尔和日期/时间。
1. 数字型
数字类型可以存储整数、实数和浮点数,可以表示数值的大小,参与计算。
1.1 BINARY_INTEGER
我们可以使用 BINARY_INTEGER 数据类型来存储有符号整数。它的范围是 -231至231。跟 PLS_INTEGER 一样, BINARY_INTEGER 所需的存储空间也要小于NUMBER。但是,大多数的 BINARY_INTEGER 操作要比 PLS_INTEGER 操作慢。
BINARY_INTEGER子类型
所谓的基类型,就是有子类型继承于它。子类型在基类型的基础上添加一些约束限制,也可能重新定义数值范围。为了使用方便,PL/SQL 预定义了下面几个 BINARY_INTEGER 的子类。
NATURAL
(非负整数,允许 NULL
)
限制变量只能存储 0 及正整数,可赋值 NULL
。
NATURALN
(非负整数,不允许 NULL
)
与 NATURAL
类似,但禁止赋值 NULL
,仅允许 0 及正整数。
POSITIVE
(正整数,允许 NULL
)
限制变量只能存储 大于 0 的正整数,可赋值 NULL
(与 NATURAL
的区别:不包含 0)。
POSITIVEN
(正整数,不允许 NULL
)
与 POSITIVE
类似,但禁止赋值 NULL
,仅允许大于 0 的正整数。
SIGNTYPE
(三态整数:-1、0、1,允许 NULL
)
限制变量只能存储 -1、0、1 三个值,常用于表示“负/零/正”三态逻辑。
子类型NATURAL和POSITIVE能让我们将一个整数变量的取值范围分别限制在非负数和正整数之内。NATURALN和POSITIVEN不允许为整数类型变量赋空值。SIGNTYPE把整数的取值范围限定在-1,0,1,这在编程中很适合表示三态逻辑(tri-state logic)。
1.2 NUMBER
我们可以使用NUMBER数据类型来存储定点或浮点数。它的范围是1E-130至10E125。如果表达式的值超过这个范围,我们就会得到数字溢出错误(a numeric overflow or underflow error)。我们可以为要存储的数字指定精度,包括数字的全长和小数长度。语法如下:
NUMBER[(precision,scale)]
其中precision表示数字的总长度,scale代表可以有几位小数。如果要使用浮点数的话,就不能指定长度和精度,像下面这样声明就可以了:
NUMBER
声明整数直接使用下面的语法:
|
|
不可以用常量或变量指定NUMBER的长度和精度。NUMBER类型最大的长度是38位。如果不指定NUMBER类型的最大长度,就会默认采用这个长度或是使用系统所支持的最大长度。
scale的范围从-84到127,能够决定舍入规则。例如,一个scale值为2的数字,舍入后的小数部分将是最接原小数部分的百分位数 (3.456舍入为3.46)。如果scale是负数,它就会从小数点左边开始进行舍入操作。如scale值为-3的数字舍入后的结果将是最接近原值的千位数(3456舍入为3000)。当数字本身是整数时,scale 为零的数字舍入后结果还是本身;若数字含小数部分,scale=0 会舍入到整数(可能改变原值)。如果不指定 scale 的值,默认就为 0。
|
|
NUMBER子类型
为了能和ANSI/ISO和IBM类型兼容或是想使用一个更加有描述性意义的名字,我们就可以使用下面的NUMBER子类型。
- DEC
- DECIMAL
- DOUBLE PRECISION
- FLOAT
- INTEGER
- INT
- NUMERIC
- REAL
- SMALLINT
对定点数(DEC/DECIMAL/NUMERIC):“精度”= 整数位数 + 小数位数(固定位数,如DEC(8,2)即 8 位总位数)。
对浮点数(DOUBLE PRECISION/FLOAT/REAL):“精度”= 十进制有效数字个数(如 126 位二进制≈38 位有效数字,示例需控制有效数字≤38 位)。例如:1.2345678901234567890123456789012345678×10³⁴,因为浮点数仅需存储 “有效数字部分(1.2345…)” 和 “指数(34)”,但指数不算做精度,因此精度以 “有效数字个数” 衡量,而非 “整数 + 小数的总位数”。
有效数字包含小数点前后的有意义数字,从第一个非 0 数字开始计数(前面的 0 是 “占位符”,不算有效数字,如00123.45的有效数字是 5 位:1、2、3、4、5);小数点后的数字,只要在第一个非 0 数字之后,均算有效数字(如123.45的有效数字是 5 位,0.0123的有效数字是 3 位);末尾的 0 是否算有效数字,取决于是否有意义(如123.00的有效数字是 5 位,代表精确到百分位;12300若未标注,则可能是 3 位有效数字,末尾 0 为占位符)。
DEC
(定点数,同DECIMAL
)
DEC
是NUMBER
的子类型,用于声明定点数,默认精度为10,可指定精度和小数位数(DEC(精度, 小数位)
),最大精度38位。
DECIMAL
(定点数,同DEC
)
与DEC
完全等价,用于兼容ANSI标准,同样支持定点数声明。
DOUBLE PRECISION
(浮点数)
用于声明高精度浮点数,最大精度为126位二进制(约38位十进制),适合需要高浮点精度的场景。
FLOAT
(浮点数)
可指定二进制精度(FLOAT(b)
,b
范围1-126),默认精度126位二进制(约38位十进制),与DOUBLE PRECISION
类似。
INTEGER
(整数)
用于声明整数,精度最大38位十进制,无小数部分,等价于NUMBER(38, 0)
。
INT
(整数,同INTEGER
)
INT
是INTEGER
的简写,功能完全一致,用于声明整数。
NUMERIC
(定点数,同DEC
/DECIMAL
)
与DEC
、DECIMAL
功能相同,用于声明定点数,强调“数值型”以兼容ANSI标准。
REAL
(浮点数)
精度为63位二进制(约18位十进制),是FLOAT(63)
的简写,适合中等精度浮点场景。
SMALLINT
(短整数)
本质仍是NUMBER
子类型,通常用于表示较小的整数(精度约等同于NUMBER(38, 0)
,但语义上强调“短”以兼容其他数据库)。
使用DEC、DECIMAL和NUMBERIC可以声明最大精度为38位十进制数字的定点数字。而使用DOUBLE PRECISION和FLOAT可以声明最大精度为126位二进制数字的浮点数字,大约相当于38位十进制数字。或是使用REAL声明最大精度为63位二进制数字的浮点数字,大约相当于18位十进制数字。INTEGER、INT和SMALLINT可以声明最大精度为38位十进制数字的整数。
PLS_INTEGER
我们可以使用PLS_INTEGER数据类型来存储有符号整数。它的取值范围在 -231至231 之间。PLS_INTEGER所需的存储空间要比NUMBER少,运算的速度要高于NUMBER和BINARY_INTEGER。虽然PLS_INTEGER和BINARY_INTEGER的取值范围一样,但它们不完全兼容。PLS_INTEGER在运算时如果有溢出,则会有异常抛出,而BIANRY_INTEGER发生溢出时,如果结果是要赋给一个NUMBER类型的变量时,就不会有异常抛出。为了考虑兼容性,我们仍可以在旧的应用程序中使用BINARY_INTEGER;但在新的应用程序中, PLS_INTEGER会带来更好的性能。
2. 字符型
字符类型可以存放字符和数字混合的数据,表现词和文章,操作字符串。
2.1 CHAR
我们可以使用CHAR类型来存储定长的字符数据。但该数据的内部表现形式是取决于数据库字符集的。CHAR类型有一个用于指定最大长度的可选参数,长度范围在1到32767字节之间。我们可以采用字节或字符的形式来设置该参数。语法如下:
CHAR[(maximum_size [CHAR | BYTE] )]
maximum_size不能是常量或变量,只能是范围在1到32767之间的整数文字。
如果我们不指定最大值,它默认是1。如果我们用字节形式指定最大值,有时就会出现空间不足的问题(多字节字符会占用多于一个字节的空间)。为了避免这样的问题发生,我们可以采用按照字符的方式指定其最大值,这样,即使是那些包含多个字节的参数可以被灵活地存储下来。按照字符指定长度的方式,上限大小仍旧是32767字节,所以,对于双字节和多字节的字符集,我们可以使用字节最大长度的一半或三分之一作为最大字符个数。
虽然PL/SQL字符变量的长度相对来说比较长,但CHAR类型在数据库的字段中最大存储长度为2000个字节,所以,我们不能往数据库CHAR类型字段中插入超过2000个字节的字符。但是,我们可以把任意CHAR(n)插入LONG类型的字段中,因为LONG的最大长度是2**31字节或是2G (gigabyte)。如果我们不使用CHAR或BYTE来对字符类型长度进行限制的话,初始化参数NLS_LENGTH_SEMANTICS会决定默认长度大小的。CHAR的子类型CHARACTER和CHAR有着相同的取值范围。也就是说,CHARACTER只是CHAR的一个别名而已。这个子类型能与ANSI/ISO和IBM类型相兼容。
|
|
2.2 LONG和LONG RAW
我们可以使用LONG类型来存储变长的字符串。除了LONG类型的最大长度是32760字节(约32KB)之外,LONG类型和VARCHAR2很相像。我们还可以使用LONG RAW类型来存储二进制数据或二进制字符串。LONG RAW和LONG类似,但是它不会被PL/SQL解析。LONG RAW的最大长度也是32760字节。从9i开始,LOB类型变量可以与LONG和LONG RAW类型交换使用。Oracle推荐将LONG和LONG RAW都对应的转换成COLB和BLOB类型。
我们可以将LONG类型的值插入字段类型为LONG的数据库中,因为在数据库中LONG的长度是2^31 字节既即 2GB;但是,不可以从LONG字段中检索超过32760字节(约 32KB)的字符放到LONG类型变量中去。同样,对于LONG RAW类型来说,这条规则同样适用,在数据库中它的最大长度也是2^31字节,而变量的长度在32760字节(约 32KB)以内。LONG类型字段可以存储文本、字符数组或短文等。我们可以对LONG字段进行UPDATE、INSERT和SELECT操作,但不能在表达式(如 LONG字段 + ‘abc’)、SQL函数调用(如 UPPER(LONG字段)、或某个SQL子句(如 WHERE LONG字段 = ‘xxx’、GROUP BY LONG字段)中使用它。
注意:在SQL语句中,PL/SQL会将LONG类型的值绑定成VARCHAR2类型,而不是LONG。但是,如果被绑定的VARCHAR2值超过 4000 个字节,Oracle会自动地将绑定类型转成LONG,但LONG并不能应用在SQL函数中,所以,这时我们就会得到一个错误消息。
第一步:创建含LONG字段的表
先在数据库中创建一张表,其中content
字段为LONG
类型(用于存大文本):
第二步:插入数据(INSERT操作,支持)
用PL/SQL向LONG
字段插入文本(注意:PL/SQL的LONG变量最大32760字节,所以插入的文本不能超过这个长度,否则需用其他方式如DBMS_LOB
):
|
|
第三步:查询数据(SELECT操作,支持,但要避坑)
从LONG
字段查询数据时,若内容≤32760字节,可存到PL/SQL的LONG变量;若超过,则会报错(后面说解决办法):
|
|
输出结果(类似):
|
|
坑点验证:若数据库LONG字段内容超过32760字节,用PL/SQL变量接收会报错:
|
|
第四步:更新数据(UPDATE操作,支持)
更新LONG
字段的内容,同样需注意PL/SQL变量的长度限制:
|
|
第五步:验证操作限制(禁止的操作)
下面是错误示例,展示LONG
字段不能做的操作(执行会报错):
|
|
补充:如何解决“PL/SQL变量存不下大LONG数据”的问题?
如果数据库LONG字段的内容超过32760字节(比如存了一篇100KB的文章),直接用PL/SQL的LONG变量接收会报错,此时需用 DBMS_LOB
包(Oracle提供的处理大对象的工具),步骤如下:
|
|
RAW
我们可以使用RAW数据类型来存储二进制数据或字节串。例如,一个RAW类型的变量可以存储一个数字化图形。RAW类型数据和VARCHAR2类型数据类似,只是PL/SQL不对其进行解析而已。同样,在我们把RAW数据从一个系统传到另一个系统时,Oracle Net也不会对它做字符集转换。RAW类型包含了一个可以让我们指定最大长度的可选参数,上限为32767字节,语法如下:
RAW(maximum_size)
我们不能使用常量或变量来指定这个参数;而且参数的范围必须是在1到32767范围内。在数据库中RAW类型字段的最大长度是2000个字节,所以,不可以把超过2000字节的内容放到RAW类型字段中。我们可以把任何RAW类型插入到LONG RAW类型的数据库字段中,因为LONG RAW在数据库中是2^31字节既2G,但是不能把超过32767字节的LONG RAW类型放入RAW变量中。
2.3 ROWID和UROWID
在Oracle内部,每个数据表都有一个伪列ROWID,用于存放被称为ROWID的二进制值。每个ROWID代表了一行数据的存储地址。物理 ROWID能够标识普通数据表中的一行信息,而逻辑ROWID能够标识索引组织表(index-organized table)中的一行信息。其中ROWID类型只能存储物理内容,而UROWID(universal rowid)类型可以存储物理,逻辑或外来(non-Oracle)ROWID。
建议:只有在旧的应用程序中,为了兼容性我们才使用ROWID数据类型。对于新的应用程序,应该使用UROWID数据类型。
当我们把查询出来的ROWID放到ROWID变量时,可以使用内置函数ROWIDTOCHAR,这个函数能把二进制内容转换成18个字节的字符串;还有一个与之对应的CHARTOROWID函数,可以对该过程进行反操作,如果转换过程中发现字符串并不是一个有效的ROWID时,PL/SQL就会抛出预定义异常SYS_INVALID_ROWID。UROWID变量和字符串之间进行转换也可以直接使用赋值操作符。这时,系统会隐式地实现UROWID和字符类型之间的转换。
物理ROWID(Physical Rowid)可以让我们快速的访问某些特定的行。只要行存在,它的物理ROWID就不会改变。高效稳定的物理ROWID在查询行集合、操作整个集合和更新子集是很有用的。例如,我们可以在UPDATE或DELETE语句的WHERE子句中比较UROWID变量和ROWID伪列来找出最近一次从游标中取出的行数据。
物理ROWID有两种形式。10字节扩展ROWID格式(10-byte extended rowid format)支持相对表空间块地址并能辨识分区表和非分区表中的行记录。6字节限定ROWID格式支持向后兼容。
扩展ROWID使用检索出来的每一行记录的物理地址的base-64编码。例如,在SQL*Plus(隐式地将ROWID转换成字符串)中的查询:
OOOOOOFFFBBBBBBRRR这样的形式有四部分组成:
“000000"代表数据对象号(data object number),如上例中的"AAAAqc”,能够辨识数据库段。同一段中的模式对象,都有着相同的数据对象号。
“FFF"代表文件号(file number),如上例中的"AAB”,能辨识出包含行的数据文件。在数据库中,文件号是唯一的。
“BBBBBB"代表块号(block number),如上例中的"AAADFN”,能辨识出包含行的数据块。块号是与它们所在的数据文件相关,而不是表空间。所以,两个在同一表空间的行数据,如果它们处于不同的数据文件中,也可能有着相同的块号。
“RRR"代表了行号(row number),如上例中的"AAH”,可以辨识块中的行数据。
逻辑ROWID为访问特定行提供了最快的访问速度。Oracle在索引组织表基础上使用它们构建二级索引。逻辑ROWID没有持久的物理地址,当新数据被插入时,它的值就会在数据块上偏移。但是,如果一个行数据的物理位置发生变化,它的逻辑ROWID就无效了。
2.4 VARCHAR2
我们可以使用VARCHAR2类型来存储变长的字符数据。至于数据在数据库中的内部表现形式要取决于数据库的字符集。VARCHAR2类型需要指明数据长度,这个参数的上限是32767字节。语法如下:
|
|
我们不能使用常量或变量来指定maximum_size值,maximum_size值的有效范围在1到32767之间。
对于长度不同的VARCHAR2类型数据,PL/SQL对它们的处理方式也是不同的。值小的PL/SQL会优先考虑到它的性能问题,而对于值大的 PL/SQL会优先考虑到内存的使用效率问题。截止点 (cutoff point) 为2000个字节。在2000字节以下,PL/SQL会一次性分配我们声明时所指定大小的空间容纳实际的值;2000字节或2000字节以上时,PL/SQL会动态的分配VARCHAR2的存储容量。比如我们声明两个VARCHAR2类型的变量,一个长度是1999字节,一个是2000字节,如果我们把长度为500字节的值分别分配给这两个变量,那么前者占用的空间就是1999字节而后者只需500字节。
如果我们采用字节形式而非字符形式指定最大值时,VARCHAR2(n)变量就有可能太小而不能容纳n个多字节字符。为了避免这个问题,就要使用 VARCHAR2(n CHAR)形式进行定义,这样,即使字符中包含多字节字符也不会出问题。所以,对于双字节或多字节字符集,我们可以指定单字节字符集中字符个数的 1/2或 1/3。
虽然PL/SQL字符变量相对比较长,但VARCHAR2类型的数据库字段最大长度为4000个字节。所以,不能把字节超过4000的VARCHAR2类型值插入VARCHAR2类型的数据库字段中。
我们可以把任何VARCHAR2(n)值插入一个LONG类型的数据库字段,因为LONG字段最大长度为2**31字节。但是,不能把LONG字段中检索出来的长度超过32767字节的值放到VARCHAR2(n)变量中。
如果声明时不使用CHAR或BYTE限定修饰词,初始化参数NLS_LENGTH_SEMANTICS会决定默认的设置。当PL/SQL过程编译时,这个参数的设置就会被记录下来,这样,当过程失效后被重新编译时就会被重新使用。
VARCHAR2的子类型
下面VARCHAR2的子类型的范围与VARCHAR2完全相同,它们只是VARCHAR2的一个别名而已。
- STRING
- VARCHAR
我们可以使用这些子类型来与ANSI/ISO和IBM类型兼容。注意:目前,VARCHAR和VARCHAR2有着相同意义,但是在以后的 PL/SQL版本中,为了符合SQL标准,VARCHAR有可能会作为一个单独的类型出现。所以最好使用VARCHAR2,而不是VARCHAR。
3. 本地字符型
被广泛使用的单字节ASCII和EBCDIC字符集很适合表现罗马字符,但有些亚洲语言,如汉语、日语等包含了成千上万个字符,这些语言中的一个字符就需要用两个或三个字节来表示。为了处理这些语言,Oracle提供了全球化支持,允许我们处理单字节和多字节字符数据,并在字符集之间进行数据转换。 Oracle还能让我们的应用程序运行在不同的语言环境中。
有了全球化支持,数字和日期格式会根据用户会话中所指定的语言约定(language convention)而自动进行调节。因此,全世界的用户可以使用他们母语来使用Oracle。Oracle支持一种数据库字符集和一种国家特有字符集两种,前者用于标识符和源代码,后者用于国家特有语言数据。NCHAR和NVARCHAR2类型用于存储本地字符集。
对比维度 | 数据库字符集(Database Character Set) | 国家字符集(National Character Set) |
---|---|---|
核心存储内容 | 标识符(表名、列名)、源代码、普通文本 | 国家特有语言数据(中文、日文、阿拉伯文等) |
支持的数据类型 | CHAR、VARCHAR2、CLOB、LONG | NCHAR、NVARCHAR2、NCLOB |
数据库创建时是否必须指定 | 是(不指定则用默认值,如AL32UTF8) | 否(但推荐指定,默认与数据库字符集关联) |
多语言支持能力 | 有限(若用单字节字符集如US7ASCII,无法存中文) | 强(默认用UTF-16/UTF-8,支持所有语言) |
典型使用示例 | 表名EMP 、列名EMP_ID 、文本'Salary' |
中文'员工姓名' 、日文'社員番号' |
创建含两种字符集类型的表
插入多语言数据
|
|
查询数据(验证字符集正常显示)
查询结果(正常显示多语言):
emp_id | emp_name | emp_name_cn | emp_name_jp |
---|---|---|---|
1 | Tanaka Taro | 田中太郎 | 田中 太郎 |
2 | Wang Wei | 王伟 | 王 偉 |
注意,当在数据库的不同字符集之间转换CHAR或VARCHAR2数据时,要确保数据保持良好的形式(well-formed)。
比较 UTF8 vs AL16UTF16
|
|
国家字符集专门存储 Unicode 多语言数据,依赖两种编码方式: UTF8 和 AL16UTF16。
编码方式 | 字符占用空间 | 优势 | 劣势 | 适用场景 |
---|---|---|---|---|
AL16UTF16 | 固定2字节/字符 | 长度计算简单(1字符=2字节),避免截断错误 | 存储ASCII字符(如英文)占用空间多(本可1字节却用2字节) | 多语言混合场景(如中日英混存),优先保证可靠性 |
UTF8 | 变长(1-3字节/字符) | 存储单字节字符(如英文)更省空间 | 长度计算复杂(同字符数可能占不同字节),易发生截断错误 | 以单字节语言(如英文)为主,需节省空间的场景 |
每个使用AL16UTF16编码的字符都占用2个字节。这将简化字符串的长度计算,避免采用混合语言编程时发生截断错误,但是这会比ASCII字符所组成的字符串需要更多空间。每个使用UTF8编码的字符占用1、2或3个字节。这就能让我们把更多的字符放到变量或数据表的字段中,但这只是在大多数字符用单字节形式表现的条件下才能做到。这种编码在传递数据到字节缓冲器时可能会引起截断错误。
Oracle公司推荐使用默认的AL16UTF16编码,这样可以获取最大的运行时可靠性。如果想知道一个Unicode字符串占用多少字节,就要使用LENGTHB函数,而不是LENGTH。
NCHAR(定长国家字符类型)
- 作用:存储固定长度的多语言字符(不足长度自动补空格)。
- 语法:
NCHAR[(maximum_size)]
,maximum_size
是字符数(不是字节),必须用整数文字(不能用常量/变量)。 - 长度限制:
- 物理上限:32767字节(PL/SQL变量中)。
→ 因编码不同,最大字符数不同:- AL16UTF16(2字节/字符):最多32767÷2≈16383字符;
- UTF8(平均1-3字节/字符):最多32767÷3≈10922字符。
- 数据库字段上限:2000字节(插入超过会报错)。
- 物理上限:32767字节(PL/SQL变量中)。
- 默认值:不指定
maximum_size
时,默认1个字符。
我们用NCHAR类型来储存定长国家特有字符数据。数据的内部表现取决于数据库创建时指定的国家特有字符集,字符集可能采用变长编码(UTF8)或定长编码(AL16UTF16)。因为这种类型总是与多字节字符兼容,所以我们可以使用它支持任何Unicode字符数据。NCHAR数据类型可接受一个可选参数来让我们指定字符的最大长度。语法如下:
|
|
因为物理限制是32767个字节,所以在AL16UTF16编码格式下最大长度为32767/2,UTF8编码格式下是32767/3。
我们不能使用常量或变量来指定最大值,只能使用整数文字。
如果我们没有指定最大长度,它默认值就为1。这个值总是代表字符的个数,不像CHAR类型,既可以采用字符形式又可以采用字节形式。
|
|
NCHAR在数据库字段中的最大宽度是2000字节。所以,我们不能向NCHAR字段中插入值超过2000字节的内容。
如果NCHAR的值比NCHAR字段定义的宽度要小,Oracle就会自动补上空格,填满定义的宽度。
我们可以在语句和表达式中交互使用CHAR和NCHAR值。从CHAR转到NCHAR总是安全的,但在NCHAR值转换到CHAR的过程中,如果CHAR类型不能完全表现NCHAR类型的值,就会引起数据丢失。这样的数据丢失会导致字符看起来像问号(?)。
NVARCHAR2(变长国家字符类型)
- 作用:存储可变长度的多语言字符(不足长度不补空格)。
- 语法:
NVARCHAR2(maximum_size)
,maximum_size
是字符数(不是字节),必须用整数文字。 - 长度限制:
- 物理上限:32767字节(PL/SQL变量中)。
→ 最大字符数同NCHAR:AL16UTF16≈16383字符,UTF8≈10922字符。 - 数据库字段上限:4000字节(比NCHAR字段宽松)。
- 物理上限:32767字节(PL/SQL变量中)。
- 必须指定长度:没有默认值,声明时必须写
maximum_size
。
我们可以使用NVARCHAR2数据类型来存储变长的Unicode字符数据。数据的内部表现取决于数据库创建时所指定的国家特有字符集,它有可能采用变长编码(UTF8)或是定长编码(AL16UTF16)。因为这个类型总与多字节兼容,我们可以用它来支持Unicode字符数据。
NVARCHAR2数据类型需要接受一个指定最大大小的参数。语法如下:
|
|
因为物理限制是32767个字节,所以在AL16UTF16编码格式下最大长度为32767/2,UTF8编码格式下是32767/3。
我们不能使用常量或变量来指定最大值,只能使用整数文字。
最大值总是代表字符的个数,不像CHAR类型,既可以采用字符形式又可以采用字节形式。
|
|
NVARCHAR2在数据库字段中的最大宽度是4000字节。所以,我们不能向NVARCHAR2字段中插入长度超过4000字节的值。
NCHAR/NVARCHAR2(国家字符类型)和CHAR/VARCHAR2(普通字符类型)可以互相转换,但有风险:
- 安全转换:从CHAR→NCHAR、VARCHAR2→NVARCHAR2(因为国家字符集支持更多语言,普通字符集的内容都能被包含)。
- 风险转换:从NCHAR→CHAR、NVARCHAR2→VARCHAR2(若普通字符集不支持国家字符集中的字符,会丢失数据,显示为问号“?”)。
我们可以在语句和表达式中交互使用VARCHAR2和NVARCHAR2值。从VARCHAR2向NVARCHAR2转换总是安全的,但在 NVARCHAR2值转换到VARCHAR2的过程中,如果VARCHAR2类型不能完全表现NVARCHAR2类型的值,就会引起数据丢失。这样的数据丢失会导致字符看起来像问号(?)。
4. LOB类型
LOB(large object)数据类型BFILE、BLOB、CLOB和NCLOB可以最大存储4G的无结构数据(例如:文本、图形、视频剪辑和音频等)块。并且,它们允许高效地随机地分段访问数据。
LOB类型和LONG、LONG RAW类型相比有几个不同的地方。比如,LOB(除了NCOLB)可以作为对象类型的一个属性,但LONG类型不可以。LOB的最大值是4G,而LONG只有2G。LOB支持随机访问数据,但LONG只支持顺序访问。
LOB类型中可以存储了LOB定位器,它能够指向存放于外部文件中的"大对象",in-linein-line(行内存储)既LOB 数据的 “全部内容” 或 “部分小内容”,直接存储在它所属的 “表行数据所在的数据块” 中,不需要额外的物理块存储 LOB 数据 或 out-of-line(行外存储)的形式既LOB 数据的 “全部内容” 存储在 “独立的数据块” 中(这些块专门用于存储 LOB 数据,称为 LOB 段),而 LOB 所属的表行数据中,只存储一个 “LOB 定位器(LOB Locator)”—— 这个定位器相当于 “地址”,指向独立 LOB 数据块的位置。BLOB、CLOB、NCLOB 或 BFILE 类型的数据库字段存储了定位器。其中 BLOB、CLOB和NCLOB 的数据存在数据库中,in-line (inside the row) 或 out-of-line (outside the row) 的形式,而 BFILE 数据存在数据库之外的操作系统文件中。
PL/SQL是通过定位器来操作LOB的。例如,当我们查询出一个BLOB值,只有定位器被返回。如果在事务中得到定位器,LOB定位器就会包含事务的ID号,这样我们就不能在另外一个事务中更新LOB内容了。同样,我们也不能在一个会话中操作另外一个会话中的定位器。
从9i开始,我们也可以把CLOB类型转成CHAR和VARCHAR2类型或是把BLOB转成RAW,反之亦然,这样,我们就能在大多数SQL和PL/SQL语句和函数中使用LOB类型了。要读、写和分段的操作LOB,我们可以使用Oracle系统包DBMS_LOB。
BFILE
BFILE数据类型用于存储二进制对象,它将存储的内容放到操作系统的文件中,而不是数据库内。每个BFILE变量都存储一个文件定位器,它指向服务器上的一个大的二进制文件。定位器包含目录别名,该别名给出了文件全路径。
BFILE类型是只读的,而且它的大小要依赖于系统,不能超过4G。我们的DBA要确保给定的BFILE存在且Oracle有读取它的权限。
BFILE并不参与事务,是不可恢复,不能被复制。能够被打开的BFILE最大数是由Oracle初始化参数SESSION_MAX_OPEN_FILES决定的。
BLOB、CLOB和NCLOB
BLOB数据类型可以在数据库中存放不超过4G的大型二进制对象;CLOB和NCLOB可以在数据库中分别存储大块CHAR类型和NCHAR类型的字符数据,都支持定宽和变宽字符集。同BFILE一样,这三个类型也都储存定位器,指向各自类型的一个大数据块。数据大小都不能超过4G。BLOB、 CLOB和NCLOB都可以在事务中使用,能够被恢复和复制。DBMS_LOB包可以对它们更改过的内容进行提交或回滚操作。BLOB、CLOB和 NCLOB的定位器都可以跨事务使用,但不能跨会话使用。
5. 布尔类型
布尔类型能存储逻辑值TRUE、FALSE和NULL(NULL代表缺失、未知或不可用的值)。只有逻辑操作符才允许应用在布尔变量上。
数据库SQL类型并不支持布尔类型,只有PL/SQL才支持。所以就不能往数据库中插入或从数据库中检索出布尔类型的值。
我们还可以使用各种函数来操作日期时间类型,如EXTRACT等。
6. 使用日期和时间子类型来避免"切断"问题
对于某些日期和时间类型的默认精度都要小于它们对应的最大精度。例如,DAY TO SECOND的默认精度是DAY(2) TO SECOND(6),而最大精度是DAY(9) TO SECOND(9)。为了避免在使用这些类型进行赋值和传递参数时丢失精度,我们可以声明它们的子类型,这些类型都采用了最大的精度:
- TIMESTAMP_UNCONSTRAINED
- TIMESTAMP_TZ_UNCONSTRAINED
- TIMESTAMP_LTZ_UNCONSTRAINED
- YMINTERVAL_UNCONSTRAINED
- DSINTERVAL_UNCONSTRAINED
二、用户自定义子类型
每个PL/SQL基类型都有对应的值集合和操作符集合。子类同样会指定同其基类型相同的值集合和操作符集合的子集作为它自己的值集合和操作符集合。所以说子类并不是一个新类型,它只是在基类的基础上添加了一个可选的约束。
子类可以增加可读性和兼容性。PL/SQL在STANDARD包里预定义了一些子类型。如下例:
子类型CHARACTER和基类型完全一样,所以CHARACTER是一个未作约束的子类型。但是,子类型INTEGER将基类NUMBER的值集合的子集作为自己的值集合,所以INTEGER是一个约束的子类型。
1. 定义子类型
我们可以在任何PL/SQL块、子程序或包中定义自己的子类型,语法如下:
|
|
subtype_name就是声明的子类型的名称,base_type可以是任何标量类型或用户定义类型,约束只是用于限定基类型的精度和数值范围,或是最大长度。下面举几个例子:
|
|
我们可以使用%TYPE或%ROWTYPE来指定基类型。当%TYPE提供数据库字段中的数据类型时,子类型继承字段的大小约束(如果有的话)。但是,子类型并不能继承其他约束,如NOT NULL。
2. 使用子类型
一旦我们定义了子类型,我们就可以声明该类型的变量、常量等。下例中,我们声明了Counter类型变量,子类型的名称代表了变量的使用目的:
下面的例子演示了如何约束用户自定义子类型:
子类型还可以检查数值是否越界来提高可靠性。下例中我们把子类型Numeral的范围限制在-9到9之间。如果程序把这个范围之外的数值赋给Numeral类型变量,那么PL/SQL就会抛出一个异常。
|
|
3. 类型兼容
一个未作约束的子类型是可以和它的基类型交互使用。例如下面的声明,amount值可以在不用转换的情况下直接赋给total:
如果基类型相同,那么不同的子类型也是可以交互使用。例如,下面的声明中,finished的值就可以赋给debugging:
不同的子类型也是有可能交互使用,只要它们的基类型属于同一个数据类型种类。例如下面的声明中,verb值就能赋给sentence:
三、 数据类型转换
有时我们需要把一个值从一个类型转换成另一个类型。例如,如果我们想检查一个ROWID,我们就必须把它转成一个字符串。PL/SQL支持显式和隐式(自动的)数据类型转换。
1. 显式转换
如果要进行类型间的转换,我们可以使用内置函数。例如将一个CHAR类型的值转换成一个DATE或NUMBER类型的值,我们就可以使用函数TO_DATE和TO_NUMBER。反过来,如果从DATE或NUMBER转成CHAR的话,可以使用TO_CHAR函数。
2. 隐式转换
PL/SQL有时会帮助我们进行隐式地数据类型转换。下面的例子中,CHAR型变量start_time和finish_time都有一个代表从午夜开始后所经过的秒数的值。这两个变量的差值要放到elapsed_time中去。所以,PL/SQL会自动地把CHAR类型转换成NUMBER类型。
|
|
另外,在把查询的结果赋给变量之前,如果有必要的话PL/SQL也会把原值类型转成对应的变量类型,典型的例子就是把DATE类型放入VARCHAR2变量中。
同样,在把变量值赋给数据库字段时,PL/SQL在必要的情况下,也会进行数据类型转换。如果PL/SQL无法确定采用哪种转换形式,就会发生变异错误。这种情况下,我们就必须使用类型转换函数。表格中的 “X” 代表 “支持隐式转换”(即 PL/SQL 会自动完成转换,无需手动写函数)。
源类型\目标类型 | BIN_INT | BLOB | CHAR | CLOB | DATE | LONG | NUMBER | PLS_INT | RAW | UROWID | VARCHAR2 |
---|---|---|---|---|---|---|---|---|---|---|---|
BIN_INT | X | X | X | X | |||||||
BLOB | X | ||||||||||
CHAR | X | X | X | X | X | X | X | X | X | X | |
CLOB | X | X | |||||||||
DATE | X | X | |||||||||
LONG | X | X | X | ||||||||
NUMBER | X | X | X | X | X | ||||||
PLS_INT | X | X | X | X | X | ||||||
RAW | X | X | X | ||||||||
UROWID | X | X | |||||||||
VARCHAR2 | X | X | X | X | X | X | X | X | X |
注意,列表中只列出表现不同的类型,而那些表现相同的类型,如CLOB和NCLOB,CHAR和NCHAR,还有VARCHAR2和NVARCHAR2都是可以互相替换。
要在CLOB和NCLOB之间转换,我们必须使用转换函数TO_CLOB和TO_NCLOB。
TIMESTAMP,TIMESTAMP WITH TIME ZONE,TIMESTAMP WITH LOCAL TIME ZONE,INTERVAL DAY TO SECOND和INTERVAL YEAR TO MONTH 都可以作为DATE类型用同样的转换规则进行转换。但是由于它们的内部表现形式不同,这些类型不能相互转换。
3. 显式转换 VS 隐式转换
通常,依赖隐式的数据转换不是一个好习惯,因为这样做会引起性能问题并可能随着软件版本变更而影响到后续的版本。隐式转换是与环境相关的,它的转换结果有时是无法预知的。所以,最好还是使用类型转换函数。这样的话,程序就有更好的可读性和可维护性。
4. DATE值
当把一个查询出来的DATE类型值放到CHAR或VARCHAR2变量中时,PL/SQL必须将内置的二进制值转成字符值,它就会调用 TO_CHAR将日期按照默认的日期格式转成字符值。同样,把一个CHAR或VARCHAR2插入到DATE类型的字段中,也是需要进行类型转换的, PL/SQL会调用TO_DATE将字符按照默认的日期格式转成日期类型的值。如果对转换有特殊要求,我们就得显式地指明转换格式。
5. RAW和LONG
RAW值在把查询出来的RAW或LONG RAW类型值赋给CHAR类型或VARCHAR2类型变量的时候,PL/SQL必须把内置的二进制值转成字符值。这种情况下,PL/SQL会把每个RAW 或LONG RAW类型的二进制字节转成等值的一对十六进制字符值。比如PL/SQL会把二进制11111111转换成字符"FF"。函数RAWTOHEX也具有这样的功能。把CHAR或VARCHAR2类型值插入RAW或LONG RAW字段时也是需要类型转换的。变量中的每对字符要转成等值二进制字节,如果无法进行正常的转换,PL/SQL就会抛出异常。简单说二进制数据 “穿了十六进制字符的外衣”,就能被 CHAR/VARCHAR2 存储;反之,“外衣”(十六进制字符)脱掉后,又能变回二进制数据存入 RAW。
小结
在数据库时代,特别是Oracle数据库时代,PL/SQL已经成为了数据库应用程序开发的标准语言。
文章作者 会写代码的小郎中
上次更新 2017-09-18
许可协议 CC BY-NC-ND 4.0