1. &与&&的区别:
一个&的意思 是每次碰到这个变量,都问问你是啥
两个&,那么只问你一次,以后这个变量都按这个值处理
2.声明常量
eg: declare testConstant CONSTANT NUMBER :=300
begin
end;
3. IF语句
IF CONDITION1 THEN
STATEMENT 1;
ELSIF CONDITION2 THEN
STATEMENT2;
ELSIF CONDITION3 THEN
STATEMENT3;
ELSE
STATEMENT4;
END IF;
4.CASE语句
a.CASE
CASE selector
WHEN EXPRESSION 1 THEN STATEMENT 1;
WHEN EXPRESSION 2 THEN STATEMENT 2;
...
WHEN EXPRESSION N THEN STATEMENT N;
ELSE STATEMENT N+1;
END CASE;
eg:
case v_num_flg
when 0 then
dbms_output.put_line('.........');
else
dbms_output.put_line('......');
end case;
b.搜索式CASE语句
CASE
WHEN SEARCH CONDITION 1 THEN STATEMENT1;
WHEN SEARCH CONDITION 2 THEN STATEMENT2;
WHEN SEARCH CONDITION 3 THEN STATEMENT3;
..........
ELSE
STATEMENT N+1;
END CASE;
c.CASE表达式:与CASE语句的差别是:END CASE改为END即可
5.NULLIF与COALESCE
NULLIF(expression1,expression2):如果expression1等于expression2,则NULLIF返回NULL。如果expression1不等于expression2,NULLIF函数返回expression1;
NULLIF函数存在一个限制:不能把字面值NULL赋予expression1.
nvl()将空值转换为指定的值nvl(exp1,exp2),第一个为null,取第二个
nvl2()将空值转换为指定的值nvl2(exp1,exp2,exp3) ,第一个为null,取第二个,第二个也为null,取第三个
COALESCE(expression1,expression2,.....,expressionN)
6.迭代控制--------LOOP
a. LOOP(方式一:通过EXIT语句)
LOOP
STATEMENT1;
STATEMENT2;
IF CONDITION THEN
EXIT;
END IF;
END LOOP;
b.LOOP(方式二:通过EXIT WHEN 语句)
LOOP
STATEMENT 1;
STATEMENT 2;
EXIT WHEN CONDITION;
END LOOP;
7.迭代控制--------WHILE
a.普通WHILE语句
WHILE CONDITION LOOP
STATEMENT1;
STATEMENT2;
...
STATEMENTN;
END LOOP;
b.含EXIT的while语句
WHILE TEST_CONDITION LOOP
STATEMENT1;
STATEMENT2;
IF EXIT_CONDITION THEN
EXIT;
END IF;
END LOOP;
c.含EXIT WHEN的WHILE语句
WHILE TEST_CONDITION LOOP
STATEMENT1;
STATEMENT2;
EXIT WHEN EXIT_CONDITION;
END LOOP;
8.迭代控制------数值型FOR循环
其中:REVERSE为从大到小,默认为从小到大,另:
loop_counter不能被其他语句使用,它只在循环时作为一个计数器
a. 普通的数值型for循环
FOR loop_counter IN [REVERSE] lower_limit..upper_limit LOOP
STATEMENT1;
...
STATEMENTN;
END LOOP;
b.含EXIT语句的数值型for循环
FOR loop_counter IN [REVERSE] lower_limit..upper_limit LOOP
STATEMENT1;
STATEMENT2;
IF EXIT_CONDITION THEN
EXIT;
END IF;
END LOOP;
c.含EXIT语句的数值型for循环
FOR loop_counter IN [REVERSE] lower_limit..upper_limit LOOP
STATEMENT1;
STATEMENT2;
EXIT WHEN EXIT_CONDITION;
END LOOP;
9.迭代控制------CONTINUE语句
只有当处于循环中时,CONTINUE和CONTINUE WHEN语句才会有效,当处于循环之外时,会带来语法错误。为避免这个错误,可以使用RETURN语句。
CONTINUE和CONTINUE WHEN语句适用于所有类型的循环
a. 与EXIT类似的:
IF CONTINUE_CONDITION THEN
CONTINUE;
END IF;
b.与EXIT WHEN类似:
CONTINUE WHEN CONTINUE_CONDITION;
10.显式游标--低级
处理显式游标的步骤:
a.声明游标:在内存中建立游标的初始化环境
eg:
DECLARE CURSOR c_student is select firstname||' '||last_name name FROM student
注:声明记录类型: vr_student c_student%ROWTYPE .与游标c_student的类型一致
b.打开游标:打开被声明的游标,并分配内存
eg:
OPEN c_student
c.检索游标:从被声明与打开的游标检索数据
eg:
方式一:
LOOP
FETCH c_student INTO vr_student;
DBMS_OUTPUT.PUTLINE(vr_student.name);
EXIT WHEN c_student%NOTFOUND;
END LOOP;
方式二:
FOR vr_student IN c_student
LOOP
...
END LOOP;
d.关闭游标:释放所分配的内存
CLOSE c_student;
显式游标属性:
%NOTFOUND cursor_name%NOTFOUND boolean类型
%FOUND cursor_name%FOUND boolean类型
%ROWCOUNT cursor_name%ROWCOUNT 从游标中所检索的记录数量
%ISOPEN cursor_name%ISOPEN boolean类型
11.显式游标----高级
a.传参
eg:
CURSOR c_zip(p_state IN zipcode.state%TYPE) is
SELECT ZIP,CITY,STATE
FROM ZIPCODE
WHERE STATE=p_state
传参方式:
OPEN c_zip('NY')
或者
FOR r_zip IN c_zip('NY')
LOOP
.....
b. FOR UPDATE:当希望使用游标来更新数据库的表时,只能使用FOR UPDATE字句。目的是锁定希望更新的数据库表中数据行
语法: FOR UPDATE OF <item_name>
eg:
1.declare cusrsor c_course is select course_no,cost from course for update
2.
declare cusrsor c_course is select course_no,cost from course for update of phone 只更新表course中的phone字段
c.WHERE CURRENT OF 与 FOR CURRENT OF
当希望更新最新检索的数据行时,可以使用FOR CURRENT OF。只能在FOR UPDATE OF 游标中使用WHERE CURRENT OF 。WHERE CURRENT OF字句的好处是,免于在UPADATE语句中使用WHERE字句。
eg:
DECLARE CURSOR c_stud_zip IS select s.student_id,z.city FROM student s, zipcode z WHERE z.city='BROOKLYN' AND s.zip=z.zip FOR UPDATE OF phone
BEGIN
FOR r_stud_zip IN c_stud_zip
LOOP
UPDATE STUDENT SET PHONE='123'||SUBSTR(phone,4)
WHERE CURRENT OF c_stud_zip;
END LOOP;
END;
13.引用游标
DECLARE
TYPE cusor_type IS REF CURSOR [RETURN return_type];
cursor_variable cursor_type
single_record return_type;
BEGIN
OPEN cursor_variable FOR query_definition (eg:select * from empdfn) [USING bind_argument1,bind_argument2,...];
LOOP
FETCH cursor_variable;
EXIT WHEN cursor_variable%NOTFOUND;
...
END LOOP;
CLOSE cursor_variable;
END;
12.触发器
CREATE [OR REPLACE] TRIGGER Trigger_name
{BEFORE|AFTER} Triggering_event ON table_name
[REFERENCING NEW|OLD AS NEW_RECORD|OLD_RECORD]
[FOR EACH ROW]
[FOLLOWS another_trigger]
[ENABLE/DISABLE]
[WHEN condition]
DECLARE
declaration statements
BEGIN
executable statements
EXCEPTION
exception-handling statements
END;
:NEW为引用最新的列值
:OLD为引用以前的列值
以上两个变量只有在使用了FOR EACH ROW的时候才有效,UPDATE可以使用:NEW与:OLD,INSERT只可以使用:NEW,DELETE只可以使用:OLD
----
两类:
行触发器和语句触发器
使用INSTEAD OF 触发器:定义与数据库视图的
eg:
CREATE OR REPLACE TRIGGER instrustructor_sum_del
INSTEAD OF DELETE ON instructor_sum_view
FOR EACH ROW
BEGIN
DELETE FROM instructor WHERE instructor_id= :OLD.instructor_id;
END;
13.复合触发器
eg:
CREATE OR REPLACE TRIGGER student_compound
FOR INSERT ON STUDENT
COMPOUND TRIGGER
v_day VARCHAR2(12);
BEFORE STATEMENT IS
BEGIN
...
--语句级触发器,不能用:OLD与:NEW
END BEFORE STATEMENT;
BEFORE EACH ROW IS
BEGIN
... --行触发器,能够使用:OLD与:NEW
END BEFORE EACH ROW;
END student_compound;
14.集合
a.联合数组
TYPE type_name IS TABLE OF element_type [NOT NULL] INDEX BY element_type;
table_name TYPE_NAME;
eg:
DECLARE
TYPE last_name_type IS TABLE OF student.last_name%TYPE INDEX BY BINARY_INTERGE;
last_name_tab last_name_type;
b.嵌套数组
TYPE type_name IS TABLE OF element_type [NOT NULL];
table_name TYPE_NAME;
注:必须先初始化嵌套表
eg:
DECLARE
TYPE last_name_type IS TABLE OF student.last_name%TYPE;
last_name_tab last_name_type := last_name_type(); --初始化嵌套表
c.集合的方法
如果某特定元素存在于集合中,则EXISTS会返回TRUE。该方法可用于避免SUBSCRIPT_OUTSIDE_LIMIT异常
COUNT返回集合中元素的数量
EXTEND会扩展集合的规模
DELETE会删除集合中所有元素,指定范围的元素,或者特定元素。注意,PL/SQL会保存删除元素的占位符
FIRST 和 LAST 会返回集合中第一个和最后一个元素的下标。注意,如果嵌套表的第一个元素被删除,则FIRST方法会返回大于1的值。如果嵌套表删除
一个中间元素,则LAST方法的返回值会大于COUNT方法的返回值
PRIOR和NEXT会返回指定集合下标的前序和后续下标。
TRIM会从集合的末尾删除一个,或者指定数量的元素。注意,PL/SQL不会保存被删除元素的占位符。
eg:
DECLARE
TYPE index_by_type IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;
index_by_table index_by_type;
TYPE nested_type IS TABLE OF NUMBER;
nested_table nested_type := nested_type(1,2,3,4,5,6,7,8,9,10);
BEGIN
FOR i IN 1..10 LOOP
index_by_table(i) := i;
END LOOP;
IF index_by_table.EXISTS(3) THEN
DBMS_OUTPUT.PUT_LINE ('......');
END IF;
nested_table.DELETE(10);
nested_table.DELETE(1,3);
index_by_table.DELETE(10);
DBMS_OUTPUT.PUT_LINE('nested_table.LAST='||nested_table.LAST);
DBMS_OUTPUT.PUT_LINE('nested_table.PRIOR(2)='||nested_table.PRIOR(2));
DBMS_OUTPUT.PUT_LINE('nested_table.NEXT(2)='||nested_table.NEXT(2));
d.变长数组
TYPE type_name IS {VARRAY | VARYING ARRAY} (size_limit) OF element_type [NOT NULL];
varray_name TYPE_NAME;
注:类似于嵌套表,当变长数组被声明时,自动设置为NULL。必须在引用单个元素之前,初始化变长数组。
不能对变长数组使用DELETE方法
eg:
DECLARE
CURSOR name_cur IS
SELECT last_name
FROM student
WHERE rownum<=10;
TYPE last_name_type IS VARRAY(10) OF student.last_name%TYPE;
last_name_varray last_name_type :=last_name_type();
v_counter INTEGER := 0;
BEGIN
FOR name_rec IN name_cur LOOP
v_counter := v_counter+1;
last_name_varray.EXTEND;
last_name_varray(v_counter):=name_rec.last_name;
END LOOP;
END;
e.多层集合
eg:
DECLARE
TYPE varray_type1 IS VARRAY(4) OF INTEGER;
TYPE varray_type2 IS VARRAY(3) OF varray_type1;
varray1 varray_type1 := varray_type1(2,4,6,8);
varray2 varray_type2 := varray_type2(varray1);
15.记录
a.基于表的记录 使用%ROWTYPE属性可以创建基于表和基于游标的记录
eg:
DECLARE
course_rec course%ROWTYPE;
BEGIN
SELECT * INTO course_rec FROM course WHERE course_no=25;
END
b.基于游标的记录
注:基于游标的记录依赖于特定的游标,不能在游标之前声明
eg:
DECLARE
CURSOR student_cur IS SELECT first_name,last_name FROM student WHERE rownum<=4;
student_rec student_cur%ROWTYPE;
c.用户定义的记录
语法如下:
TYPE type_name IS RECORD
(filed_name1 datatype1 [NOT NULL] [ := DEFAULT EXPRESSION],
filed_name2 datatype2 [NOT NULL] [ := DEFAULT EXPRESSION],
...
filed_nameN datatypeN [NOT NULL] [ := DEFAULT EXPRESSION]);
record_name TYPE_NAME;
d.记录兼容性
i>.对于用户定义的记录
eg:
DECLARE
TYPE name_type1 IS RECORD(first_name VARCHAR2(12),last_name VARCHAR2(15));
TYPE name_type2 IS RECORD(first_name VARCHAR2(12),last_name VARCHAR2(15));
name_rec1 name_type1;
name_rec2 name_type2;
解释:虽然name_type1与name_type2结构一样但是不能直接将name_rec1的值赋给name_rec2(即不可:name_rec2=name_rec1),应该通过但值赋值(即:name_rec2.first_name=name_rec1.first_name)
ii>.对于基于表与游标的记录
对于基于表与游标的记录赋予用户定义类型,只要两者具有相同的结构即可
e.嵌套记录 记录中含有记录或者集合
eg:
DECLARE
TYPE name_type IS RECORD(first_name VARCHAR2(15),last_name VARCHAR2(30));
TYPE person_type IS RECORD(name name_type,zip VARCHAR2(5));
person_rec person_type;
BEGIN
SELECT first_name,last_name,zip INTO person_rec.name.first_name,person_rec.name.last_name,person_rec.zip FROM student where rownum<2;
END;
f.记录的集合
eg:
DECLARE
CURSOR name_cur IS SELECT first_name,last_name FROM student WHERE rownum<=4;
TYPE name_type IS TABLE OF name_cur%ROWTYPE INDEX BY BINARY_INTEGER;
name_tab name_type;
v_counter INTEGER :=0;
BEGIN
FOR name_rec IN name_cur LOOP
v_counter := v_counter+1;
name_tab(v_counter).first_name:=name_rec.first_name;
...
END LOOP;
END;
16.动态SQL
语法结构:
EXECUTE IMMEDIATE dynamic_SQL_string
[INTO defined_variable1,defined_variable2,...]
[USING [IN | OUT |IN OUT] bind_argument1,bind_argument2,...]
[{RETURNING | RETURN} field1,field2,... INTO bind_argument1,bind_argument2,...]
注:如果不指定USING的参数的任何模式,USING子句中所列出的所有绑定参数都是IN模式的。
当EXECUTE IMMEDIATE语句包含USING和RETURNING INTO子句时,USING子句只指定IN参数
动态SQL语句的结尾不应该是分号(;),类似地,动态PL/SQL语句块的结尾不能是右斜线(/)
17.批量SQL
a. FORALL语句
FORALL loop_counter IN bounds_clause
SQL_STATEMENT [SAVE EXCEPTION];
其中,bounds_clause是下面形式之一:
lower_limit..upper_limit
INDICES OF collection_name BETWEEN lower_limit..upper_limit
VALUES OF collection_name
注:INDICES OF子句所引用的集合也许是稀疏的,即有些元素可能已经被删除
如果VALUES OF子句中所使用的集合是联合数组,它必须使用PLS_INTEGER和BINARY_INTEGER进行索引
VALUES OF子句中所使用集合的元素必须是PLS_INTEGER或者BINARY_INTEGER
当VALUES OF子句所引用的集合是空时,FORALL语句会异常
eg:
DECLARE
TYPE row_num_type IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
row_num_tab row_num_type;
BEGIN
FOR i IN 1..10 LOOP
row_num_tab(i):=i;
END LOOP;
FOR ALL i IN 1..10
INSERT INTO TEST(row_num)VALUES(row_num_tab(i));
COMMIT;
END;
i>.SAVE EXCEPTION选项
此选项能够实现:即使当对应的SQL语句导致异常,FORALL语句仍旧能够继续执行。所产生的异常被存储在名为SQL%BULK_EXCEPTIONS的游标属性中。SQL%BULK_EXCEPTION游标属性是一个记录集合,
有两个字段组成:ERROR_INDEX和ERROR_CODE。ERROR_INDEX字段会存储发生异常的FORALL语句的迭代编号,ERROR_CODE会存储对应于所抛出异常的ORACLE错误代码,可以根据SQLERRM函数查询
错误码对应的错误信息
eg: FOR i in 1.. SQL%BULK_EXCEPTIONS.COUNT LOOP
DBMS_OUTPUT.PUT_LINE('RECORD' || SQL%BULK_EXCEPTIONS(i).error_index ||'caused error'||i||':'||SQL%BULK_EXCEPTIONS(i).ERROR_CODE||' '||SQLERRM(-SQL%BULK_EXCEPTIONS(i).ERROR_CODE));
END LOOP;
ii>.INDICES OF选项
FOR i IN 1..10 LOOP
row_num_tab(i):=i;
END LOOP;
row_num_tab.DELETE(1);
row_num_tab.DELETE(5);
row_num_tab.DELETE(7);
FORALL i IN INDICES OF
row_num_tab
INSERT INTO TEST(ROW_NUM) VALUES(row_num_tab(i));
COMMIT;
由于删除1,5,7三个元素,故FORALL会迭代7次
iii>.VALUES OF 选项 VALUES OF 可指明FORALL语句中循环计数器的值来自于所指定集合中元素的值
b.BULK COLLECT语句 BULK COLLECT会检索多行数据,这些数据行存储在集合变量中
eg:
DECLARE
TYPE first_name_type IS TABLE OF student.first_name%TYPE;
TYPE last_name_type IS TABLE OF student.last_name%TYPE;
first_name_tab first_name_type;
last
_name_tab last_name_type;
BEGIN
select first_name,last_name BULK COLLECT INTO first_name_tab ,last_name_tab FROM student;
FOR i IN first_name_tab.FIRST..first_name_tab.LAST
LOOP
DBMS_OUTPUT.PUT_LINE('fist_name:'||first_name_tab(i));
END LOOP;
END;
17.存储过程
语法如下:
CREATE [OR REPLACE] PROCEDURE name
[(parameter[,parameter,...])]
AS
[local declarations]
BEGIN
executable statements
[EXCEPTION
exception handlers]
END [name];
18.函数
语法如下:
CREATE [OR REPLACE] FUNCTION function_name
[parameter...]
RETURN datatype
IS
BEGIN
<body>
RETURN [return_value]
END;
19.包
a.包规范语法:
CREATE OR REPLACE PACKAGE package_name
IS
[declarations of variables and types]
[specifications of cursors]
[specifications of modules]
END [package_name];
b.包体语法:
CREATE OR REPLACE PACKAGE BODY package_name
IS
[declarations of variables and types] 注:只在包体中声明,但在包规范中未声明的变量、函数、存储过程作为私有变量(即只能被包体内的函数、存储过程调用)
[specifications of cursors]
[specifications of modules]
[BEGIN executable statements]
[EXCEPTION
exception handlers]
END [package_name];
eg:
CREATE OR REPLACE PACKAGE test_pkg
AS
PROCEDURE find_name(i_student_id IN student.student_id%TYPE);
FUNCTION id_is_good(i_student_id IN student.studend_id%TYPE)
RETURN BOOLEAN;
END test_pkg;
AS
PROCEDURE find_name(i_student_id IN student.student_id%TYPE)
IS
...
BEGIN
...
END find_name;
FUNCTION id_is_good(i_student_id IN student.studend_id%TYPE)
RETURN BOOLEAN
IS
...
BEGIN
...
END
id_is_good;
END test_pkg;
20.使用RESTRICT_REFERENCES编译指令实现纯度等级
语法如下:
PRAGMA RESTRICT_REFERENCES( function_name,WNDS [,WNPS] [,RNDS] [,RNPS])
注:该函数必须存储在数据库中,而不是存储在ORACLE工具的库中
函数必须是行函数,而不是列函数或分组函数
对于所有函数(不管是否在SQL语句中使用),参数必须属于IN模式
eg:
CREATE OR REPLACE PACKAGE school_api
AS
FUNCTION new_id
RETURN NUMBER;
PRAGMA RESTRICT_REFERENCES(new_id,WNDS,WNPS,RNDS,RNPS);
END school api;
WNDS:write no database state,不会修改数据库表
WNPS:write no package state,不会修改任何包变量的值
RNPS:read no package state,不会读取任何包变量
RNDS:read no database state,不会读取任何数据库表
21.在包中重载:
规则如下:
a.形参必须在参数数量、顺序或者数据类型等方面存在区别
b.不能重载独立模块的名称
c.只有return数据类型存在差别的函数不能重载
22.对象类型
a.语法如下:
CREATE [OR REPLACE] TYPE type_name AS OBJECT
(attribute_name1 attribute_type,
attribute_name2 attribute_type,
...
attribute_nameN attribute_type,
[method1 specification],
[method2 specification],
...
[methodN specification],
);
[CREATE [OR REPLACE] TYPE BODY type_name AS
method1 body;
method2 body;
...
methodN body;]
END;
eg1:
CREATE OR REPLACE TYPE test_type AS OBJECT
(no varchar2(1),name varchar2(30),grade number(2));
调用:
DECLARE
v_test_type test_type;
BEGIN
select test_type(no,name,null) into v_test_type FROM student;
END;
另对于未初始化对象,对象实例及其属性都是null,在使用默认构造器对该对象实例进行初始化之后,就不再是NULL,尽管单个值可能仍旧是NULL,eg:v_test_type :=v_test_type(null,null,null)
eg2:
CREATE OR REPLACE TYPE test_tab_type IS TABLE OF test_type;
b.对象类型方法
对象类型有个内置参数SELF,这个参数表示对象类型的特定实例。
对象比较的两种方式:映射方法与排序方法(二选一)
构造器方法,成员方法,静态方法
eg:
CREATE OR REPLACE TYPE zipcode_obj_type AS OBJECT
( zip VARCHAR2(5),city VARCHAR2(20),state VARCHAR2(2),
--构造器方法,需要两个参数
CONSTRUCTOR FUNCTION zipcode_obj_type
( SELF IN OUT NOCOPY zipcode_obj_type,zip VARCHAR2)
RETURN SELF AS RESULT,
--成员方法
MEMBER PROCEDURE get_zipcode_info
(out_zip OUT varchar2,out_city OUT VARCHAR2),
---静态方法
STATIC PROCEDURE display_zipcode_info
(in_zip_obj IN ZIPCODE_OBJ_TYPE),
--映射方法
MAP MEMBER FUNCTION zipcode RETURN VARCHAR2,
--排序方法
ORDER MEMBER FUNCTION zipcode1(zip_obj ZIPCODE_OBJ_TYPE) RETURN INTEGER
);
/
CREATE OR REPLACE TYPE BODY zipcode_obj_type AS
CONSTRUCTOR FUNCTION zipcode_obj_type
(SELF IN OUT NOCOPY zipcode_obj_type,zip VARCHAR2)
RETURN SELF AS RESULT
IS
BEGIN
SELF.zip:=zip;
SELECT city INTO SELF.city where zip=SELF.zip;
RETURN;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN;
END;
MEMBER PROCEDURE get_zipcode_info
(
out_zip OUT varchar2,out_city OUT VARCHAR2)
IS
BEGIN
out_zip := SELF.zip;
out_city :=SELF.city;
END;
STATIC PROCEDURE display_zipcode_info
(in_zip_obj IN ZIPCODE_OBJ_TYPE)
IS
BEGIN
DBMS_OUTPUT.PUT_LINE('ZIP: '||in_zip_obj.zip);
END;
MAP MEMBER FUNCTION zipcode RETURN VARCHAR2
IS
BEGIN
RETURN (zip)
END;
ORDER MEMBER FUNCTION zipcode1(zip_obj ZIPCODE_OBJ_TYPE) RETURN INTEGER
IS
BEGIN
IF zip<zip_obj.zip THEN RETURN -1;
ELSIF
zip=zip_obj.zip THEN RETURN 0;
ELSIF
zip>zip_obj.zip THEN RETURN 1;
END IF;
END;
END;
/
另: i>.调用
MAP MEMBER FUNCTION zipcode的方式为:
IF v_zip_obj1 >v_zip_obj2 THEN ... END IF;
ii>.调用
ORDER MEMBER FUNCTION zipcode1的方式为 v_result :=v_zip_obj1.zipcode1(v_zip_obj2);