PL/SQL 基础---复杂数据类型和自定义类型

发布时间 2023-04-03 19:04:39作者: eyesfree

原文地址:https://blog.csdn.net/villare/article/details/53437924

PL/SQL 基础—复杂数据类型和自定义类型
PLSQL中常用的自定义类型就两种: 记录类型、 PLSQL内存表类型(根据表中的数据字段的简单和复杂程度又可分别实现类似于简单数组和记录数组的功能)

除此之外,还有大对象类型:CLOB、BFILE

一. 内存表对象(集合)
常用函数

函数名 功能
COUNT 返回集合中元素的个数
DELETE 删除集合中所有元素
DELETE(x) 删除元素下标为x的元素 对VARRAY非法
DELETE(x,y) 删除元素下标从X到Y的元素 对VARRAY非法
EXTEND 在集合末尾添加一个元素 对Index_by非法
EXTEND(x) 在集合末尾添加x个元素 对Index_by非法
EXTEND(x,n) 在集合末尾添加元素n的x个副本 对Index_by非法
EXIST(x) 如果集合元素x已经初始化,则返回TRUE, 否则返回FALSE
FIRST 返回集合中的第一个元素的下标号,对于VARRAY集合始终返回1
LAST 返回集合中最后一个元素的下标号, 对于VARRAY返回值始终等于COUNT
LIMIT 返回VARRY集合的最大的元素个数 Index_by集合和嵌套表无用
NEXT(x) 返回在第x个元素之后及紧挨着它的元素值,如果x是最后一个元素,返回null
PRIOR(x) 返回在第x个元素之前紧挨着它的元素的值,如果x是第一个元素,则返回null
TRIM 从集合末端开始删除一个元素 对于index_by不合法
TRIM(x) 从集合末端开始删除x个元素
1 .变长数组(VARRAY) – 简单数组、下标自动生成(int)、不可从中间删除

 

-- 变长数组
TYPE type_name IS VARRAY (max_elements)
OF element_datatype [ NOT NULL ];

DECLARE
TYPE t_array IS VARRAY(5) OF NUMBER;

m_data t_array;

BEGIN
m_data := t_array(0,1,2,3,4);
FOR i IN 1..5 LOOP
DBMS_OUTPUT.put_line(m_data(i));
END LOOP;
END;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
注:

1 .VARRAY变量需要初始化存储空间

1. m_data := t_array(0,1,2,3,4);
2. m_date.EXTEND(x[,n]);
1
2
2 .下标从1开始:1..N

3 .大小不能超过类型定义的大小: TYPE t_array IS VARRAY(5) OF NUMBER

2 .索引表(Associative arrays) – 索引类型自定义(和值一一对应),可删除,下表不变

数组值和唯一的索引关联,和变长数组有下标不同

TYPE type_name IS TABLE OF element_datatype [ NOT NULL ]
--->
INDEX BY BINARY_INTEGER;
INDEX BY PLS_INTEGER;
INDEX BY POSITIVE;
INDEX BY NATURAL;
INDEX BY SIGNTYPE;
INDEX BY VARCHAR2(32767);
INDEX BY table.column%TYPE;
INDEX BY cursor.column%TYPE;
INDEX BY package.variable%TYPE;
INDEX BY package.subtype;


-- 索引表

DECLARE
TYPE t_associative_table IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;
v_table t_associative_table;

BEGIN
-- 不能使用 v_table := t_associative_table(0,1,2,2,4);
v_table(1) := 1;
v_table(2) := 2;
v_table(3) := 3;
v_table(4) := 3;
v_table(5) := 5;

FOR i IN 1..5 LOOP
IF v_table.EXISTS(i) THEN
v_table.DELETE(i);
END IF;
END LOOP;
DBMS_OUTPUT.put_line(v_table.COUNT());
END;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
3 .嵌套表(Nested tables) – 索引类型为INTEGER(>0),可删除,下标不变

可以看成特殊的索引表,不需要指定索引类型,默认为整型,且不能为负数

可以和数据库中的表相对应

-- 类型声明
TYPE type_name IS TABLE OF element_datatype [ NOT NULL ];

-- 嵌套表
DECLARE
TYPE t_nested_table IS TABLE OF NUMBER;
v_table t_nested_table;


BEGIN
v_table := t_nested_table(0,1,2,2,4);
v_table.DELETE(1);
DBMS_OUTPUT.put_line(v_table.COUNT());
FOR i IN 2..5 LOOP
IF v_table.EXISTS(i) THEN
DBMS_OUTPUT.put_line(v_table(i));
END IF;
END LOOP;

END;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
二. 记录(Record)
一条数据集合,相当于数据库中一条记录


TYPE type_name IS RECORD
(field_declaration[, field_declaration]…);
identifier type_name;

--field_declaration包括以下几种方式
field_name field_type;
field_name variable%TYPE/Table.column%TYPE;
field_name Table%ROWTYPE;
[NUT NULL]
[DEFAULT value]


--示例

DECLARE
TYPE T_RECORD_DEF IS RECORD
(
employee_id NUMBER,
lats_name VARCHAR2(20) DEFAULT 'NO_NAME');
e_record_1 T_RECORD_DEF;
e_record_2 T_RECORD_DEF;

BEGIN
SELECT e.EMPLOYEE_ID,e.Last_Name
INTO e_record_1
FROM EMPLOYEES e WHERE e.EMPLOYEE_ID = 100;

DBMS_OUTPUT.put_line(e_record_1.lats_name);
DBMS_OUTPUT.put_line(e_record_2.lats_name);
END;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
注:

字段名不区分大小写

Record类型不需要初始化;

Record可以和Table类型同时使用,构成类似表结构

合理使用%TYPE 、 TABLE%ROWTYPE(这种声明方式不会包含默认值信息);

二. 大对象类型(CLOB、NCLOB、BLOB、BFILE)
作用 使用
CLOB 字符大对象,存储在数据库内部;
NCLOB 多字节字符大对象,存储在数据库内部;
BLOB 二进制大对象,存储在数据库内部;
BFILE 二进制文件,存储在数据库外部;
-- Writw...
DECLARE
directions CLOB;
amount BINARY_INTEGER;
offset INTEGER;
first_direction VARCHAR2(100);
more_directions VARCHAR2(500);
BEGIN
--Delete any existing rows for 'Munising Falls' so that this
--example can be executed multiple times
DELETE
FROM waterfalls
WHERE falls_name='Munising Falls';

--Insert a new row using EMPTY_CLOB( ) to create a LOB locator
INSERT INTO waterfalls
(falls_name,falls_directions)
VALUES ('Munising Falls',EMPTY_CLOB( ));

--Retrieve the LOB locator created by the previous INSERT statement
SELECT falls_directions
INTO directions
FROM waterfalls
WHERE falls_name='Munising Falls';

--Open the LOB; not strictly necessary, but best to open/close LOBs.
DBMS_LOB.OPEN(directions, DBMS_LOB.LOB_READWRITE);

--Use DBMS_LOB.WRITE to begin
first_direction := 'Follow I-75 across the Mackinac Bridge.';
amount := LENGTH(first_direction); --number of characters to write
offset := 1; --begin writing to the first character of the CLOB
DBMS_LOB.WRITE(directions, amount, offset, first_direction);

--Add some more directions using DBMS_LOB.WRITEAPPEND
more_directions := ' Take US-2 west from St. Ignace to Blaney Park.'
|| ' Turn north on M-77 and drive to Seney.'
|| ' From Seney, take M-28 west to Munising.';
DBMS_LOB.WRITEAPPEND(directions,
LENGTH(more_directions), more_directions);

--Add yet more directions
more_directions := ' In front of the paper mill, turn right on H-58.'
|| ' Follow H-58 to Washington Street. Veer left onto'
|| ' Washington Street. You''ll find the Munising'
|| ' Falls visitor center across from the hospital at'
|| ' the point where Washington Street becomes'
|| ' Sand Point Road.';
DBMS_LOB.WRITEAPPEND(directions,
LENGTH(more_directions), more_directions);

--Close the LOB, and we are done.
DBMS_LOB.CLOSE(directions);
END;

---Reading...
DECLARE
directions CLOB;
directions_1 VARCHAR2(300);
directions_2 VARCHAR2(300);
chars_read_1 BINARY_INTEGER;
chars_read_2 BINARY_INTEGER;
offset INTEGER;
BEGIN
--Retrieve the LOB locator inserted previously
SELECT falls_directions
INTO directions
FROM waterfalls
WHERE falls_name='Munising Falls';

--Begin reading with the first character
offset := 1;

--Attempt to read 229 characters of directions, chars_read_1 will
--be updated with the actual number of characters read
chars_read_1 := 229;
DBMS_LOB.READ(directions, chars_read_1, offset, directions_1);

--If we read 229 characters, update the offset and try to
--read 255 more.
IF chars_read_1 = 229 THEN
offset := offset + chars_read_1;
chars_read_2 := 255;
DBMS_LOB.READ(directions, chars_read_2, offset, directions_2);
ELSE
chars_read_2 := 0;
directions_2 := '';
END IF;

--Display the total number of characters read
DBMS_OUTPUT.PUT_LINE('Characters read = ' ||
TO_CHAR(chars_read_1+chars_read_2));

--Display the directions
DBMS_OUTPUT.PUT_LINE(directions_1);
DBMS_OUTPUT.PUT_LINE(directions_2);
END;


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
BFILE处理
需要提前建立DIRCTORY,并且当前用户有读取此DIRECTORY的权限

CREATE DIRECTORY bfile_data AS '文件目录 如:c:\data\file.txt';

GRANT READ ON DIRECTORY bfile_data TO 当前用户名;

--使用
DECLARE
web_page BFILE;
BEGIN
--Delete row for Tannery Falls so this example can
--be executed multiple times
DELETE FROM waterfalls WHERE falls_name='Tannery Falls';

--Invoke BFILENAME to create a BFILE locator
web_page := BFILENAME('BFILE_DATA','Tannery Falls.htm');

--可以打开文件进行读写操作

--Save our new locator in the waterfalls table
INSERT INTO waterfalls (falls_name, falls_web_page)
VALUES ('Tannery Falls',web_page);
END;

--读写
DECLARE
web_page BFILE;
html RAW(60);
amount BINARY_INTEGER := 60;
offset INTEGER := 1;
BEGIN
--Retrieve the LOB locat0r for the web page
SELECT falls_web_page
INTO web_page
FROM waterfalls
WHERE falls_name='Tannery Falls';

--Open the locator, read 60 bytes, and close the locator
DBMS_LOB.OPEN(web_page);
DBMS_LOB.READ(web_page, amount, offset, html);
DBMS_LOB.CLOSE(web_page);

--Uncomment following line to display results in hex
--DBMS_OUTPUT.PUT_LINE(RAWTOHEX(html));

--Cast RAW results to a character string we can read
DBMS_OUTPUT.PUT_LINE(UTL_RAW.CAST_TO_VARCHAR2(html));
END;


--写入记录
DECLARE
Tannery_Falls_Directions BFILE
:= BFILENAME('BFILE_DATA','TanneryFalls.directions');
directions CLOB;
destination_offset INTEGER := 1;
source_offset INTEGER := 1;
language_context INTEGER := DBMS_LOB.default_lang_ctx;
warning_message INTEGER;
BEGIN
--Delete row for Tannery Falls, so this example
--can run multiple times.
DELETE FROM waterfalls WHERE falls_name='Tannery Falls';

--Insert a new row using EMPTY_CLOB( ) to create a LOB locator
INSERT INTO waterfalls
(falls_name,falls_directions)
VALUES ('Tannery Falls',EMPTY_CLOB( ));

--Retrieve the LOB locator created by the previous INSERT statement
SELECT falls_directions
INTO directions
FROM waterfalls
WHERE falls_name='Tannery Falls';

--Open the target CLOB and the source BFILE
DBMS_LOB.OPEN(directions, DBMS_LOB.LOB_READWRITE);
DBMS_LOB.OPEN(Tannery_Falls_Directions);

--Load the contents of the BFILE into the CLOB column
DBMS_LOB.LOADCLOBFROMFILE
(directions, Tannery_Falls_Directions,
DBMS_LOB.LOBMAXSIZE,
destination_offset, source_offset,
NLS_CHARSET_ID('US7ASCII'),
language_context, warning_message);

--Check for the only possible warning message.
IF warning_message = DBMS_LOB.WARN_INCONVERTIBLE_CHAR THEN
dbms_output.put_line(
'Warning! Some characters couldn''t be converted.');
END IF;

--Close both LOBs
DBMS_LOB.CLOSE(directions);
DBMS_LOB.CLOSE(Tannery_Falls_Directions);
END;

 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
DBMS_LOB常用函数:具体用法参看相关文档

函数 功能
APPEND 将源LOB中的内容加到目的LOB中
CLOSE 关闭已经打开的LOB
FILECLOSE 关闭打开的BFILE定位符所指向的OS文件
FILECLOSEALL 关闭当前会话已经打开的所有BFILE文件
FILEEXISTS 确定file_loc对应的OS文件是否存在,1:存在 0:不存在
FILEGETNAME 获取BFILE定位符所对应的目录别名和文件名
FILEISOPEN 确定BFILE对应的OS文件是否打开
FILEOPEN 打开文件
COPY 从源LOB中复制数据到目的LOB
ERASE 删除LOB中全部或部分内容
TRIM 将LOB值减少到指定的长度
WRITE 向LOB中写入数据
INSTR 返回特定样式数据从LOB某偏移位置开始出现N次的具体位置
OPEN 打开LOB,open_mode(只读:dbms_lob.lob_readonly,写读dbms_lob.lob_readwrite)
GETLENGTH 获取LOB的长度
READ 从LOB中读出数据
SUBSTR 与字符处理函数SUBSTR使用方法一样
WRITEAPPEND 将缓冲区数据写到LOB尾部
注:
CLOB等大对象不能直接使用IS NULL判空(EMPTY_CLOB() IS NULL), 正确的判断如下:


IF some_clob IS NULL THEN
--There is no data
ELSEIF DBMS_LOB.GETLENGTH(some_clob) = 0 THEN
--There is no data
ELSE
--Only now is there data
END IF;
1
2
3
4
5
6
7
8
9
CLOB,NCLOB处理和字符串类似:支持|| 、 substr等对字符串的操作;
————————————————
版权声明:本文为CSDN博主「villare」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/villare/article/details/53437924