#自定义类型

自定义类型是数据库里的一种PL/SQL对象,简称UDT。

分布式部署中不支持UDT。

UDT可以定义的数据结构种类包括:

  • 对象结构Object:面向对象概念的抽象数据类型(Abstract Data Type),包含属性和方法,UDT之间可以继承或嵌套。需要创建类型和类型主体两部分,且必须先创建类型,再创建类型主体。
  • 数组结构Varray: 一种需定义最大数量、包含可变长度的多个元素的集合类型 ,除可被创建为一个独立的UDT对象外,还可以作为集合变量中的数组变量直接在过程体内进行声明。只需要创建类型部分。
  • 嵌套表结构Nested Table: 一种未定义最大数量、包含多行的可变长度的多个元素的集合类型 ,除可被创建为一个独立的UDT对象外,还可以作为集合变量的嵌套表变量直接在过程体内进行声明。只需要创建类型部分。

UDT的初始化

系统对每一个自定义类型自动创建一个同名的构造函数,在对象实例化时进行初始化。

UDT的实例化

将数据类型声明为UDT的一个变量,即为该UDT的实例,对变量进行初始化赋值即为该UDT的实例化。

UDT的依赖

当一个UDT被表或者其他UDT引用时,该UDT就成为被依赖对象,无法重建、修改或删除(除非使用FORCE选项)。

对于继承性依赖,如父类型强制修改或重建,子类型也将自动重建。

对于嵌套性依赖,如被依赖的UDT强制修改时,依赖的UDT将自动失效。

UDT的权限

拥有CREATE TYPE权限可以在自己的模式下创建UDT,拥有CREATE ANY TYPE权限才可以在其它模式下创建UDT。拥有EXECUTE ANY TYPE权限才可以使用其它模式的UDT。

在其它模式下创建UDT时,UDT所属用户必须直接拥有相关权限,此时UDT所属用户从角色继承的权限不会生效。

# 创建UDT类型

通过CREATE TYPE语句创建一个UDT类型,包括UDT的定义和编译,如果UDT编译失败,依然会被创建,但在使用时将报错。

create type::=

syntax
CREATE OR REPLACE EDITIONABLE NONEDITIONABLE TYPE schema . type_name FORCE invoker_rights_clause object_base_type_def object_subtype_def

invoker_rights_clause::=

syntax
AUTHID CURRENT_USER DEFINER

object_base_type_def::=

syntax
AS IS object_type_def varray_type_spec nested_table_type_def

object_type_def::=

syntax
OBJECT ( attribute datatype , , element_spec ) NOT FINAL

element_spec::=

syntax
constructor_spec subprogram_spec map_order_function_spec constructor_spec subprogram_spec map_order_function_spec

constructor_spec::=

syntax
CONSTRUCTOR FUNCTION type_name ( SELF IN OUT datatype , parameter datatype , ) RETURN SELF AS RESULT

subprogram_spec::=

syntax
MEMBER STATIC procedure_spec function_spec

map_order_function_spec::=

syntax
MAP ORDER MEMBER function_spec

varray_type_spec::=

syntax
VARRAY VARYING ARRAY ( size_limit ) OF datatype NOT NULL

nested_table_type_def::=

syntax
TABLE OF ( datatype NOT NULL )

object_subtype_def::=

syntax
UNDER schema . supertype ( attribute datatype , , element_spec ) NOT FINAL

# or replace

当要创建的UDT已经存在时,将其进行重建。

# editionable | noneditionable

用于语法兼容,无实际含义。

# schema

包含UDT的模式名称,可省略,则默认为当前登录用户的模式。

# type_name

要创建的UDT的名称,在同一模式下的UDT不能有相同的名称。不可省略,且需符合YashanDB的对象命名规范

# force

当类型名称存在且被其它对象依赖时,REPLACE FORCE选项可以强制替换UDT的定义。 如果类型被表依赖时,不能使用REPLACE FORCE进行替换。

# invoker_rights_clause

首页invoker_rights_clause部分描述。

# object_base_type_def

创建模式级别的UDT。

# object_type_def

指定OBJECT关键字可以创建一个Object类型的UDT。

构成Object的成员变量称为属性,定义Object行为的成员子程序称为方法。

一个UDT必须至少包含一个属性,可以不包含任何方法。

# attribute

Object包含的属性名称。每个Object类型的UDT至少包含一个属性,且属性的名称在该UDT内唯一。

# datatype

属性的数据类型,可以是系统内置的数据类型,或之前已定义的某个UDT(此种情况称为UDT嵌套,当前类型将对该UDT形成依赖)。

# element_spec

指定Object包含的方法,可以为构造方法、静态方法和成员方法。

  • 方法参数的默认值最大长度是8000。
  • 方法参数的默认值中不能使用当前类型。

构造方法

每个UDT都会存在一个由系统创建的与类型同名的默认构造函数,用于UDT的初始化工作。

同时,用户也可以自定义一个构造函数,其名称必须与类型同名。 当自定义构造函数的参数/属性个数、类型与默认的构造函数完全一致时,系统将先调用自定义构造函数。

示例

CREATE OR REPLACE TYPE udt_object AS OBJECT (
	area_no CHAR(2),
    CONSTRUCTOR FUNCTION udt_object(SELF IN OUT udt_object,a VARCHAR) RETURN SELF AS RESULT
);
/

CREATE OR REPLACE TYPE BODY udt_object AS
	CONSTRUCTOR FUNCTION udt_object(SELF IN OUT udt_object,a VARCHAR) RETURN SELF AS RESULT AS
    BEGIN
		self.area_no := '0'||a;
		RETURN;
	END;
END;
/

DECLARE
	obj1 udt_object;
BEGIN
	obj1 := udt_object(3);
	DBMS_OUTPUT.PUT_LINE('area no is: '||obj1.area_no);
END;
/

--result
area no is: 03

静态方法

静态方法为UDT的全局方法,无self参数,不能由对象实例(声明为该UDT的变量)调用。

可以声明为一个静态的存储过程或静态的函数。

示例

CREATE OR REPLACE TYPE udt_object_static AS OBJECT (
	area_no CHAR(2),
    STATIC FUNCTION showAreaStatic(a VARCHAR) RETURN VARCHAR
);
/
CREATE OR REPLACE TYPE BODY udt_object_static AS
	STATIC FUNCTION showAreaStatic(a VARCHAR) RETURN VARCHAR IS
    BEGIN
		RETURN '0'||a;
	END;
END;
/

DECLARE
	obj udt_object_static;
BEGIN
	obj := udt_object_static(3);
	DBMS_OUTPUT.PUT_LINE('area_no is: '||udt_object_static.showAreaStatic(obj.area_no));
END;
/

--result
area_no is: 03

成员方法

成员方法为对象实例可调用的方法,默认存在一个self参数(函数为IN,存储过程为IN OUT)。

可以声明为一个成员存储过程或成员函数,当为成员函数时,可声明为如下两类特殊的成员函数:

  • MAP函数:用于将对象实例映射为标量数值,MAP函数不可以定义参数,且返回类型不可以为LOB型。
  • ORDER函数:用于对两个对象实例进行比较,ORDER函数必须声明一个类型为其所在UDT的参数,且只能为一个。其返回类型只能为INT。

一个UDT中,MAP函数和ORDER函数不能同时存在,且最多只能存在一个。

示例

--map函数
CREATE OR REPLACE TYPE udt_object FORCE AS OBJECT (
	area_no CHAR(2),
    MAP MEMBER FUNCTION showArea RETURN VARCHAR
) NOT FINAL;
/
CREATE OR REPLACE TYPE BODY udt_object AS
	MAP MEMBER FUNCTION showArea RETURN VARCHAR AS
    BEGIN
		RETURN self.area_no;
	END;
END;
/

DECLARE
	obj1 udt_object;
	obj2 udt_object;
BEGIN
	obj1 := udt_object(3);
	obj2 := udt_object('a');
	IF obj1.showArea > obj2.showArea THEN
		DBMS_OUTPUT.PUT_LINE('compare result: '||obj1.area_no);
	ELSE
		DBMS_OUTPUT.PUT_LINE('compare result: '||obj2.area_no);
	END IF;
END;
/

--result
compare result: a 

--order函数
CREATE OR REPLACE TYPE udt_object1 AS OBJECT (
	area_no CHAR(2),
    ORDER MEMBER FUNCTION orderArea(a udt_object1) RETURN INT
);
/
CREATE OR REPLACE TYPE BODY udt_object1 AS
	ORDER MEMBER FUNCTION orderArea(a udt_object1) RETURN INT AS
    BEGIN
		CASE 
		WHEN self.area_no = a.area_no THEN RETURN 0;
		WHEN self.area_no > a.area_no THEN RETURN 1;
		WHEN self.area_no < a.area_no THEN RETURN -1;
		END CASE;
	END;
END;
/

DECLARE
	obj1 udt_object1;
	obj2 udt_object1;
BEGIN
	obj1 := udt_object1(3);
	obj2 := udt_object1('a');
	DBMS_OUTPUT.PUT_LINE('compare result: '||obj1.orderArea(obj2));
END;
/

--result
compare result: -1
# [not] final

指定NOT FINAL表示可以为此类型创建子类型。默认为FINAL,即不能为此类型创建子类型。

# varray_type_spec

指定VARRAY或[VARYING] ARRAY关键字可以创建一个数组类型的UDT。数组是一组有序元素的集合,长度限定,每个元素都为datatype所指定的数据类型。

除datatype可以为普通标量数据类型,及已定义的UDT外,数组类型UDT的定义、使用及操作方法均与在过程体内声明的集合变量类型中的数组类型一致。

示例

-- 创建一个数组类型,此类型的元素是上例中的udt_object,数组的元素上限是10个。
CREATE OR REPLACE TYPE udt_varray AS VARRAY(10) OF udt_object;
/

-- 通过默认构造函数给udt_varray类型的变量赋值。
DECLARE
    arr udt_varray;
BEGIN
	arr := udt_varray(udt_object(1),udt_object(2),udt_object(3));
    DBMS_OUTPUT.PUT_LINE('attribute of varray element:' || arr(1).area_no);
END;
/

--result
attribute of varray element:1

# nested_table_type_def

指定TABLE关键字可以创建一个嵌套表类型的UDT。嵌套表是一个不限长度的多行元素的集合,每个元素都为datatype所指定的数据类型。

除datatype可以为普通标量数据类型,及已定义的UDT外,嵌套表类型UDT的定义、使用及操作方法均与在过程体内声明的集合变量类型中的嵌套表类型一致。

示例

-- 创建一个嵌套表类型,此类型的元素是上例中的udt_varray。
CREATE OR REPLACE TYPE udt_nested_table AS TABLE OF udt_varray NOT NULL;
/

-- 通过默认构造函数给udt_nested_table类型的变量赋值,并将其赋值给另一个变量。
DECLARE
    nested_table udt_nested_table;
    nested_table2 udt_nested_table;
BEGIN
	nested_table := udt_nested_table(udt_varray(udt_object(1),udt_object(2),udt_object(3)),udt_varray(NULL));
    nested_table2 := nested_table;
    DBMS_OUTPUT.PUT_LINE('Records of nested_table2 is: '||nested_table2.count);
END;
/

--result
Records of nested_table is: 2

# object_subtype_def

创建一个已存在类型的子类型(此种情况称为UDT继承,子类型将继承父类型的属性和方法)。

子类型除属性名称不能与父类型中声明的任何属性和方法名称相同外,其他语法与创建父类型一致。

# supertype

已经存在的父类型名称,父类型必须是一个NOT FINAL的Object UDT。

示例

--创建上例中udt_object的子类型
CREATE OR REPLACE TYPE udt_object_child UNDER udt_object(branch_no CHAR(4));
/

DECLARE
	obj udt_object_child;
BEGIN
	obj := udt_object_child('02','0201');
	DBMS_OUTPUT.PUT_LINE('area is:'||obj.showArea||' branch is:'||obj.branch_no);
END;
/

--result
area is:02 branch is:0201

# 创建udt类型主体

对于在Object类型中定义的方法,必须在类型主体中对应实现才能使用。 通过CREATE TYPE BODY语句创建一个UDT类型主体,类型主体的名称必须和已创建的UDT的名称一样,否则该类型主体是无效状态。

create type body::=

syntax
CREATE OR REPLACE EDITIONABLE NONEDITIONABLE TYPE BODY schema . type_name IS AS element_spec element_spec_body element_spec element_spec_body END ;

element_spec_body::=

syntax
IS AS variable_declare BEGIN plsql_statements END ;

# or replace

当要创建的UDT主体已经存在时,将其进行重建。

# editionable | noneditionable

用于语法兼容,无实际含义。

# schema

包含UDT主体的模式名称,可省略,则默认为当前登录用户的模式。

# type_name

要创建的UDT主体的名称,在同一模式下的UDT主体不能有相同的名称。不可省略,且需符合YashanDB的对象命名规范

# element_spec

在UDT中已声明的方法

# element_spec_body

方法的实现过程,语法同存储过程自定义函数章节里的过程体语法描述。

示例

-- 创建一个OBJECT类型的自定义类型udt_object,此类型有4个属性,2个方法,一个成员存储过程,一个静态函数。
CREATE OR REPLACE TYPE udt_object IS OBJECT (
    branch_no CHAR(4),
    branch_name VARCHAR2(200),
    area_no CHAR(2),
    address VARCHAR2(200),
    MEMBER PROCEDURE showBranch,
    STATIC FUNCTION showObjectType(p1 VARCHAR2) RETURN VARCHAR2);
/

CREATE OR REPLACE TYPE BODY udt_object AS
MEMBER PROCEDURE showBranch IS
BEGIN
    DBMS_OUTPUT.PUT_LINE('branch_name:  ' || branch_name);
    DBMS_OUTPUT.PUT_LINE('address:      ' || SELF.address);
END;
STATIC FUNCTION showObjectType(p1 VARCHAR2) RETURN VARCHAR2 IS
v1 VARCHAR2(64);
BEGIN
    v1 := p1;
    DBMS_OUTPUT.PUT_LINE('This is STATIC METHOD of udt_object TYPE.');
    RETURN v1;
END;
END;
/

-- 通过默认构造函数给udt_object类型的变量赋值,调用变量的成员存储过程,调用udt_object类型的静态函数。
DECLARE
    obj1 udt_object;
BEGIN
    obj1 := udt_object('0101','Beijing','01','North Street');
    obj1.showBranch();
    DBMS_OUTPUT.PUT_LINE(udt_object.showObjectType('Shenzhen'));
END;
/

-- 创建一个数组类型,此类型的元素是自定义类型udt_object,数组的元素上限是10个。
CREATE OR REPLACE TYPE udt_varray AS VARRAY(10) OF udt_object;
/

-- 通过默认构造函数给udt_varray类型的变量赋值。
DECLARE
    arr1 udt_varray;
BEGIN
    arr1 := udt_varray(udt_object('0101','Beijing','11','North Street'), udt_object('0102','Shanghai','22','Pedestrian street of Nanjing Road'));
    DBMS_OUTPUT.PUT_LINE('attribute of varray element:' || arr1(1).address);
    -- method OF varray element
    arr1(1).showBranch();
    DBMS_OUTPUT.PUT_LINE('varray element count(INIT):' || arr1.COUNT);
    arr1.TRIM();
    DBMS_OUTPUT.PUT_LINE('varray element count(TRIM):' || arr1.COUNT);
    arr1.EXTEND(2, 1);
    DBMS_OUTPUT.PUT_LINE('varray element count(EXTEND):' || arr1.COUNT);
    DBMS_OUTPUT.PUT_LINE('attribute of varray element(copyed):' || arr1(3).address);
END;
/

# 删除udt

通过DROP TYPE BODY可以删除一个UDT类型主体。

通过DROP TYPE可以删除一个UDT类型,同时删除它存在的类型主体。