PLSQL应用程序开发.pdf
1 Oracle PL/SQL ProGramming 学习笔记学习笔记 Author:丁俊丁俊 目目 录录 序言-特点介绍.2 PART1 用 PL/SQL 设计程序.4 第一章 plsql 在 10g 中的新特性.4 第二章 建立和运行 plsql 程序.6 第三章 plsql 语言基本原理.9 PART2 PL/SQL 应用程序结构.12 第四章 条件和序列控制.12 第五章 循环控制.19 第六章 异常处理.23 PART3 PL/SQL 程序应用.25 第七章 用数据来工作.25 第八章 Strings.27 第九章 Numbers.31 第十章 Records.36 第十一章 集合类型.38 2 序言序言-特点介绍特点介绍 1-1 pl/sql 可以做的工作:1.用 pl/sql 的存储过程和数据库触发器实现至关重要的商业规则。2.在数据库中生成和完全地管理 xml 文档。3.web 页面与数据库的结合。4.实现自动化的数据库管理,用 pl/sql 建立安全级别来管理回滚段。1-2 pl/sql 特点:从 oracle 6 开始,模仿 Ada 语言的实现,Ada 语言强调数据抽象,信息隐藏,还有其他现代语言设计中的关键策略。pl/sql 做为 3GL 语言具有面向过程语言的许多重要特性,如:1.丰富的数据类型,从 number 到 string,从复杂的 record 到 table,以及集合类型等。2.显示的可读性强的块状结构,可以增强我们维护 plsql 程序。3.条件,循环语句,包括 if-else,3 个 loop 循环(简单 loop,for.loop,while.loop)。4.完整地异常处理机制。5.命名的,可重用的代码,如包,函数,过程,触发器,对象类型等。6.plsql 是 sql 的有力补充,与 sql 之间的联系紧密,整合性强。7.plsql 是 oracle 数据库产品的内置语言,不是一个孤立的语言。8.是一种高性能的语言。9.pl/sql 运行过程 Plsql 引擎可以接受应用程序的程序,然后将 sql 部分和 plsql 部分分离出来,分别交给 sql引擎和 plsql 引擎执行,提高执行效率。10.plsql 可以每次发送成组的 sql 语句到服务器端执行,不像 sql 每次只能发送一句,减少网络负载量,提高效率,而且在 oracle 的相关工具中,如 oracle form中,plsql 也得到增强。11.总结一句话:plsql 有应用程序的特性,是 sql 的有力补充,具有流程控制,申明和使用变量,plsql 能运行在任何具有 oracle 的环境中。3 12.plsql 支持 dml,select 语句,不直接支持 ddl,用动态语句,execute immediate,不支持权限控制如 grant 和 revoke。4 PART1 用用 PL/SQL 设计程序设计程序 第一章第一章 plsql 在在 10g 中的新特性中的新特性 1.自动的,最佳代码编译 oracle 10g 发布的第 1 版本 10.1 会重新组织代码从而提高效率。非 sql 区域的效率会提高 2倍 2.编译期警告 3.预编译功能,指定条件编译 最佳编译最佳编译(optimized compiler)在 10.1 中默认为最佳编译,可以通过修改会话 session 来实现指定编译 0 无最佳编译 1 中等最佳编译,可以消除过剩代码和异常 2 默认级别,最佳编译 ALTER SESSION SET PLSQL_OPTIMIZE_LEVEL=0;也可以指定 procedure 的编译,先设过程级别,然后用 reuse ALTER PROCEDURE bigproc COMPILE PLSQL_OPTIMIZE_LEVEL=0;然后使用 ALTER PROCEDURE bigproc COMPILE REUSE SETTINGS;编译期警告 ALTER SESSION SET plsql_warnings=enable:all 条件编译 用$IF 指定 如 CREATE OR REPLACE PROCEDURE calculate_totals IS BEGIN$IF$oe_debug AND$oe_trace_level=5$THEN DBMS_OUTPUT.PUT_LINE(Tracing at level 5 or higher);$END NULL;END calculate_totals;支持非连续的集合用 forall,在 10g 之间用 forall 只能是连续的集合,forall 与批处理 bulk collection 联合用,bulk collection 可以减少 plsql 的引擎和 sql 引擎之间的相互转换的次数,所以能提高效率。10g 之后可以是非连续的单元,用 INDICES OF 指定,如 FORALL i IN INDICES OF inactives DELETE FROM ledger WHERE acct_no=inactives(i);forall 指定集合的值,用 values of 5 FORALL i IN VALUES OF inactives_list DELETE FROM ledger WHERE acct_no=inactives(i);提高数据类型的支持 oracle 10g 的所有数字类型的全部采用机器运算,提高效率,对浮点数运算采用二进制,提高金融业计算的准确性,单精度 BINARY_FLOAT,双精度 BINARY_DOUBLE 支持 set 操作符 支持正则表达式 可以定义自己的引号处理,用 q,表示分界,指一个单引号,后面可以使用(,select q from dual;Q -Thiss a cat SQL select q_ from dual;Q_ -SQL select nqa from dual;NQ -a SQL select nq_a_ from dual;NQ -a 更多的 built_in 包,并且扩展了原来的包 DBMS_SCHEDULER 更新 DBMS_JOB.DBMS_SCHEDULER DBMS_CRYPTO 提供加密技术 DBMS_MONITOR 监控 DBMS_WARNING 编译警告控制 6 第二章第二章 plsql 开始开始 1-1 sql*plus 使用 登陆连接数据库 sqlplus dingjun123/198403 不推荐使用,暴露密码 sqlplus 会提示输入用户名,密码 sqlplus/nolog 不连接数据库,然后在 connect dingjun123/198403 不会暴露密码 show all 显示所有 sql*plus 设置 用户定义变量 define x=test;/注意与绑定变量的区别,直接定义 define x=test 也是正确的 显示 define x,显示值和类型 Define 显示所有的变量 定义是 define name=value,而绑定变量定义的时候不能赋值 引用定义的变量,用&x,要加,否则就是另一种情况了 select&x from dual;绑定变量 define x=test;variable x varchar2(10);/variable 可以简写成 var begin :x:=ding;-绑定变量有前缀:end;/select:x,&x from dual;SQL select:x,&x from dual;原值 1:select:x,&x from dual 新值 1:select:x,dsadsa from dual :X DSADSA -dd dsadsa L 显示所有缓冲区内容 n 显示第几行 del n 删除第几行 c/old/new n text 插入到第 n 行 i 插入 7 1-2 绑定变量 1.申明:直接在 salplus 中 variable x varchar2(10);不能赋值。绑定变量可以直接在 plsql 中使用,使用要加前缀:绑定变量是直接在主机环境中申明的,不能在 plsql 块中申明,在运行期赋值。一般针对可能使用很多变量,但是不清楚的情况下使用。可以提高效率。引号绑定变量必须加前缀:如:begin :x:=dj;end;/这样已经初始化了绑定变量 x,看 x 的结果用 print x 或 print:x 2.如果用函数来给绑定变量赋值可以使用 call function_name into:x,z 只有在单条语句上使用,在过程中用:x :=函数;3.在 PLSQL 中使用绑定变量有很多优点,可以从外部传参数的值,使过程的执行只进行一次硬分析,其他都是软分析,从而提高程序和语句的执行效率。1-3 创建函数 函数直接可以在 sql 中使用,有返回类型,不涉及特定表操作,与过程不一样 1.创建一个连接字符串的函数-在参数中不写 in 也可以,也可以初始化,但是不能带长度 create or replace function concatestring(a in varchar2,b in varchar2)return varchar2 as c varchar2(100);begin c:=a|b;return c;end;/-select concatestring(ding,jun)result from dual/-结果:RESULT-dingjun 注:注:set echo on 是用是用命令时候,会把调用的内容显示出来命令时候,会把调用的内容显示出来 8 a.所有能使用create语句建立的对象都可以使用drop语句来删除,如删除函数是drop function function_name;过程删除是 drop procedure procedure_name 等。b.表,函数,过程,序列,同义词,包等对象名称不能重复,否则创建不成功。如果是过程,函数,包等同类型的有占用名字的可以用 create or replace 创建,但是建立表等没有 create or replace table c.如果出错可以用 show err 来查看上次详细情况,如果全局查看可以使用 show err 对象类别 schema.名称,其中 err 是 errors 的部分。对象范围如下:SQL show error table test 用法:SHOW ERRORS FUNCTION|PROCEDURE|PACKAGE|PACKAGE BODY|TRIGGER|VIEW|TYPE|TYPE BODY|DIMENSION|JAVA SOURCE|JAVA CLASS schema.name 养成良好的习惯,写完一个对象,show error 一下 CREATE OR REPLACE program-type AS your code END;/SHOW ERRORS d.要查询定义的对象可以在 all_objects 和 user_objects 里面查询 e.所有的对象都可以使用 desc 语句在 sqlplus 中 9 第三章第三章 plsql 语言基本原理语言基本原理 1.块结构:申明和执行严格区分,在 plsql 中以 begin 为界限区分|head 命名块才有的,如函数和过程|-|declare 对变量,过程,子程序声明|execute 执行处理|exception 捕获和处理异常 块的划分:匿名块,命名块,嵌套块(在执行块里可以嵌套一个完整的块,注意变量可见域)2.标识符的限制:最好用有意义的名字,同名的要注意加前缀,如 create or replace procedure remove(name in varchar2)as begin delete a where name=name;-删除表也可以不使用 from end;这样,条件始终是真,把 a 全部删除。就算改成 a.name=name 也是一样,只能把=的右边改成能区分的,如 加上 remove.name 这样就可以避免此情况。package,function,procedure 等的参数或变量都可以通过前缀访问。特殊的可以加双引号,则使用时也加双引号,则区分大小写。如PI和pi是不同的变量。如 declare PI constant number:=3.14;-双引号区分大小写 pi constant number:=3.1415;begin dbms_output.put_line(PI is|PI|pi is|pi|pi);-pi不加双引号默认为大写 end;/结果是 PI is 3.14 pi is 3.1415 3.14 3.程序的嵌套(子程序申明)10 CREATE PROCEDURE calc_totals(fudge_factor IN NUMBER)IS subtotal NUMBER:=1;/*|申明子程序,在申明部分申明子的过程,不用create */PROCEDURE compute_running_total IS BEGIN subtotal:=subtotal+subtotal*fudge_factor;END;/*子程序结束*/BEGIN FOR mth IN 1.12 LOOP compute_running_total;END LOOP;DBMS_OUTPUT.PUT_LINE(Fudged total for year:|subtotal);END;子程序可以提高代码的可重用性和可维护性。标识符:标识符:包括常量,变量,异常,游标,程序:过程,函数,包,触发器等,保留字,标签 Plsql 中阶乘直接可以使用*,而 sql 中不可以,只能用 power 函数 规范:规范:最多 30 个字符限制(oracle 中所有限制),只能以字母开头,后面可以有下划线,$,字母,#,其他不可以,不能有空格,不能是保留字。大小写不敏感,但是要注意关键字大写,变量等小写。不能是数据库已经有的字段名,如列名.单行函数,转换函数,一般函数都可以在 plsql 中使用,但是组函数在 plsql 中只能在 sql 语句中使用,不能使用 decode。11 查看保留字可以以 DBA 身份登陆,SELECT*FROM V$RESERVED_WORDS;标量:数字,字符,日期,boolean 其中 null 对所有均适合,null 也可以作为合法语句存在。在 oracle 中 0 个字符可以表示 null,如和 null 一样,但是中间不能有空格。m varchar2(10):=和 null 一样,但是 m char(10):=和 null 就不一样了,默认有一个字符,也就是 m char(10):=其实 oracle 会自动给予 的值。oracle 中的引号 用两个引号表示一个引号,如 theres a cat-theres a cat a-a-hello-hello-oracle 10g 中对引号的处理可以:PRAGMA instruction_to_compiler;对编译的限制 标签,由内循环到外循环,由内过程到外过程,goto 等 12 PART2 PL/SQL 应用程序结构应用程序结构 第四章第四章 条件和序列控制条件和序列控制 1-1 条件语句 3 种 if-then-end if;if-then-else-end if;if-then-elsif-then-else-end if;(不是else if)注意 f,还有 else 在三种情况下可以省略。为了完整性可以加 esle null;1.条件是 boolean 类型,若是 2null 则肯定为 false,和 null 运算的都是 false 2.如果数据来自数据库,若有 null 存在,可以加上 or is null 避免或者用 nvl 函数来解决 null的问题。3.my_boolean:=condition1 AND condition2 若 condition1 为 null,则语句的值可能是 null也可能是 false 取决于 condition2 的值。短路的好处 IF condition1 OR condition2 THEN .ELSE .END IF;如果第一个为 true,则不用判断第二个 IF low_CPU_condition AND high_CPU_condition THEN .END IF;如果第一个已经确定是 false 则不会计算第二个消耗高内存的表达式 显示用短路,可以使用 nest IF low_CPU_condition THEN IF high_CPU_condition THEN .END IF;END IF;1-2 case 语句(statements)和表达式(expressions)case 语句是一种顺序执行数据,允许按条件匹配到指定的第一个表达式,然后结束。在sql 中是 sql92 的标准,但是在 oracle 的 sql 中一直到 oracle 8i 才给予支持,pl/sql 支持 case是在 oracle 9i 开始的。13 null 是什么?在 oracle 中,boolean 类型的值的确有三个:true,false,null。但是在大的关系型理论中,认为 null 不应该属于 boolean 类型,如:2=10000 THEN CASE WHEN salary 40000 THEN give_bonus(employee_id,500);WHEN salary 20000 THEN give_bonus(employee_id,1000);END CASE;-内部 case 结束 WHEN salary=10000 AND salary 20000 AND salary 40000 THEN 500 ELSE 0 END);END;注:case 表达式到 end 处为整个表达式结束,相当于整个表达式的值已经确定,这时在 end的分号之前可以进行运算,如下面的把 case 表达式的结果扩大 10 倍 16 declare m number(2):=10;n number(3);begin n:=case m when 1 then 2 when 10 then 10 end*10;-把结果扩大10倍,n的值为100。dbms_output.put_line(n);end;case 语句和 case 表达式的比较:1.语句的每个 then 之后是语句,故有分号结束,而 case 表达式是返回值,故没有分号结束 2.case 语句的 case 结束是 end case,而 case 表达式结束是单个的 case。3.case 语句中若没有 else,则整个过程中匹配不到会报 case_not_found 异常,而 case 语句没有 else 不会报异常,只是返回值为 null 而已。4.case 表达式以 end 结束为标志,可以做运算。case 在 sql 中使用 SELECT CASE WHEN DUMMY=X THEN Dual is OK ELSE Dual is messed up END FROM DUAL;正常,但是下面的混合 sql 中的 case 在 9i 的 plsql 中就会发生错误,在 10g 中已经改进,正确 DECLARE dual_message VARCHAR2(20);BEGIN SELECT CASE WHEN DUMMY=X THEN Dual is OK ELSE Dual is messed up END INTO dual_message FROM DUAL;DBMS_OUTPUT.PUT_LINE(dual_message);END;17 1-3 goto 语句 label 定义在语句之前,可以通过标签为前缀访问定义的变量,在外部变量与内部变量同名的情况下,可以加以区分。注意变量可见域 declare x number(10):=1;begin declare x number(10):=outer.x;begin outer.x:=2;dbms_output.put_line(x);-内部块的中的同名遵循就近原理 1 dbms_output.put_line(outer.x);-2 end;dbms_output.put_line(x);-外部快同名也同样 2,为outer.x end;/goto label_name;则会把逻辑无条件转到 label 所在的语句。好处是:适当地使用会增强程序的能力,但是过多使用可读性不强,goto 一般都可以用其他办法代替。如 BEGIN GOTO second_output;DBMS_OUTPUT.PUT_LINE(This line will never execute.);-永远不会执行此语句 DBMS_OUTPUT.PUT_LINE(We are here!);END;goto 使用限制:1.至少一个执行语句跟在 label 的后面 2.label 必须和 goto 语句在同一个域里 3.若是循环或嵌套,只能从内跳到外,不能从外跳到内。1-4 null 语句的使用 null;以分号结尾表示是什么都不做的语句,使用场合:1.提高程序的可读性 if-then-end if 没有 else 则不满足条件的隐性不做,但是可读性不强,若要改成可读性强的可以 if status!=0 then do something;else null;end if;18 2.捕足无效的异常 EXCEPTION WHEN exception_name1 THEN executable_statements;WHEN exception_nameN THEN executable_statements;WHEN OTHERS THEN executable_statements;-当所有的异常都未抛出的时候,可以使用 null;END;3.在标签之后使用 null 语句 PROCEDURE process_data(data_in IN orders%ROWTYPE,data_action IN VARCHAR2)IS BEGIN -First in series of validations.IF data_in.ship_date IS NOT NULL THEN status:=validate_shipdate(data_in.ship_date);IF status!=0 THEN GOTO end_of_procedure;END IF;-Second in series of validations.IF data_in.order_date IS NOT NULL THEN status:=validate_orderdate(data_in.order_date);IF status!=0 THEN GOTO end_of_procedure;END IF;.more validations.NULL;END;在条件中快速跳转到结束。19 第五章第五章 循环控制循环控制 1-1 loop 循环 pl/sql 提供三种 loop 循环 1.简单的 loop 循环 2.for.loop 循环 3.while.loop 循环 loop 基础 三种 loop 都有自己的特点和使用场合,应该深刻挖掘需求,选择哪种循环最适合。简单的 loop 循环 loop do something;if conditional then exit;/exit when conditoanl;/或抛异常或 loop 体 return 都是终止 loop end loop;特点:若不显示终止,则无限循环,简单循环始终都执行一次。下面可以强制应用程序休眠,在休眠期不执行任何循环。LOOP data_gathering_procedure;DBMS_LOCK.sleep(10);-do nothing for 10 seconds END LOOP;for counter in(cursor/select)loop for counter in reverse i.j loop 有 reverse 关键字,则是倒叙循环,从大到小,ji 步长始终是 1,若需要指定的步长,可以使用条件判断,如下面的循环步长是 2 FOR loop_index IN 1.100 LOOP IF MOD(loop_index,2)=0 THEN /*We have an even number,so perform calculation*/calc_values(loop_index);END IF;END LOOP;若是 FOR loop_counter IN REVERSE 10.1 LOOP /*不会执行此循环*/END LOOP;20 提供高低位的数字或者显示的 cursor 或者 select 语句代替高低位,如 PROCEDURE display_multiple_years(start_year_in IN PLS_INTEGER ,end_year_in IN PLS_INTEGER )IS BEGIN FOR l_current_year IN(SELECT*FROM sales_data WHERE year BETWEEN start_year_in AND end_year_in)LOOP -This procedure is now accepted a record implicitly declared -to be of type sales_data%ROWTYPE.display_total_sales(l_current_year);END LOOP;END display_multiple_years;for cursor loop 不需要指定打开,不需要指定打开,fetch,关闭游标,若有则报错。,关闭游标,若有则报错。while.loop 和简单 loop 差不多,只不过先判断条件,看是否执行 loop,先判断条件决定是否执行循环体,若条件为 false 或 null 则不执行循环。1-2 loop 标签的使用 1.循环的标签是给循环起名字,并且可以在 goto 语句中使用,当嵌套循环时候,只能从内循环跳到外层循环。2.有标签的循环,可以在 end loop 后加标签名字 3.也可以对于 exit 标签名或 exit 标签名 when 条件来结束循环 4.可以通过标签名指定 loop 索引变量。如:FOR year_number IN 1800.1995 LOOP FOR month_number IN 1.12 LOOP IF year_loop.year_number=1900 THEN-通过标签指定 loop 索引变量 exit month_loop;-退出循环加标签 END IF;END LOOP month_loop;END LOOP year_loop;-结束中加标签 21 循环问题总结:往往程序性能和循环密切相关,循环应该写的清楚明了,可读性要强,控制简单。1.使用有意义的循环索引变量 FOR focus_account IN start_id.end_id-而不是把 focus_account 写成简单的 i LOOP FOR day_in_week IN 1.7 LOOP FOR month_in_biyear IN 1.24 LOOP build_schedule(focus_account,day_in_week,month_in_biyear);END LOOP;END LOOP;END LOOP;2.用适当的方式结束循环 不要在 for 和 while loop 中使用 exit 或 exit when 终止循环,会打乱循环的结构和执行。for 用于全部查询,while 循环已经自己指定了终止条件。3.不要在循环中使用 return 或 goto 语句,那会让循环过早地结束。1 DECLARE 2 CURSOR occupancy_cur IS 3 SELECT pet_id,room_number 4 FROM occupancy WHERE occupied_dt=TRUNC(SYSDATE);5 pet_count INTEGER:=0;6 BEGIN 7 FOR occupancy_rec IN occupancy_cur 8 LOOP 9 update_bill 10 (occupancy_rec.pet_id,occupancy_rec.room_number);11 pet_count:=pet_count+1;12 EXIT WHEN pet_count=:GLOBAL.max_pets;13 END LOOP;14 END;上面的 for loop 可以显示地看出循环执行的次数。4.能用 sql 解决的不要用循环 DECLARE CURSOR checked_out_cur IS SELECT pet_id,name,checkout_date FROM occupancy WHERE checkout_date IS NOT NULL;BEGIN FOR checked_out_rec IN checked_out_cur 22 LOOP INSERT INTO occupancy_history(pet_id,name,checkout_date)VALUES(checked_out_rec.pet_id,checked_out_rec.name,checked_out_rec.checkout_date);DELETE FROM occupancy WHERE pet_id=checked_out_rec.pet_id;END LOOP;END;上面的逻辑是查询 checkout_date 不为空的所有信息,然后插入到历史表中,再清空原来的表。可以这样做:BEGIN INSERT INTO occupancy_history(pet_id,NAME,checkout_date)SELECT pet_id,NAME,checkout_date FROM occupancy WHERE checkout_date IS NOT NULL;DELETE FROM occupancy WHERE checkout_date IS NOT NULL;END;直接用 sql 完成,简单明了,而且效率也高。但是 plsql 是很灵活的,当需要捕足异常信息时候,用 for loop 就比较灵活了,直接写 sql 不知道那句执行错误,如 BEGIN FOR checked_out_rec IN checked_out_cur LOOP BEGIN INSERT INTO occupancy_history.DELETE FROM occupancy.EXCEPTION WHEN OTHERS THEN log_checkout_error(checked_out_rec);END;END LOOP;END;23 第六章第六章 异常处理异常处理 1-1 异常处理概念 oracle 中的异常分为系统异常,用户行为导致的错误,警告。当 pl/sql 程序发生错误时,执行代码要无条件地转到异常区,因此要求执行块与异常处理块要有清晰干净地把执行块和异常处理块分开。1-1-1 定义异常(defining exception)要抛出(raise)或处理(handle)异常之前,必须定义异常,oracle 预定义了成千上万种的异常,如 NO_DATA_FOUND 异常,oracle 定义的异常分配了异常代码和信息,以及相关联的名称。pl/sql 的异常是申明在 standard 包(dbms_standard)和其他内置包内,如 dbms_sql,utl_file)等 申明异常在申明区:exception_name EXCEPTION 异常处理区:begin 和 end 之间,执行区之后,用 EXCEPTION 关键字 EXCEPTION WHEN exception_name1 then do something1;WHEN exception_name2 then do something2;.WHEN OTHERS do somethingn;引用异常的名字只有在下面两种情况下才可以:抛出:raise exception_name;异常处理:when exception_name 异常的传播:没有被处理的异常,当发生时,会沿检测调用顺序向外传播。(技巧)如何使程序出现异常的时候,只终止有异常部分的代码,其他没有异常的代码,继续执行呢?考虑一个过程,对表进行 delete,update,insert 操作,若其中有一个操作出现异常,不影响其他的操作,可以将操作放在异常中。一般代码:create or replace procedure is begin delete emp where.;update emp set.;insert into emp values.;end;24 当有一个出现异常的时候,过程终止。三个操作互不影响,则可以这样写:create or replace procedure aa is begin /*每个操作都有一个自己的块,互不影响*/begin delete emp where.;exception when others then null;end;begin update emp set.;exception when others then null;end;begin insert into emp values.;exception when others then null;end;end;25 PART3 PL/SQL 程序应用程序应用 第七章第七章 用数据来工作用数据来工作 学习 pl/sql 的数据类型 number,varchar,date,boolean,record,collection,xml,user define,10g 中的新类型:BINARY FLOAT and BINARY DOUBLE.pl/sql 是一种强类型语言。在 oracle 9 之前日期类型只有 DATE,9 之后增加了 timestamp,interval(间隔),如下面的函数计算年龄。CREATE OR REPLACE FUNCTION age(dob_in IN DATE)RETURN INTERVAL YEAR TO MONTH IS BEGIN RETURN(SYSDATE-dob_in)YEAR TO MONTH;END;ref cursor 类型,分为强的和弱的,强的则指定数据结构,弱的不指定数据结构,如 DECLARE TYPE book_data_t IS REF CURSOR RETURN book%ROWTYPE;-指定数据结构 book%rowtype book_curs_var book_data_t;DECLARE TYPE book_data_t IS REF CURSOR;-未指定数据结构,为弱的 book_curs_var book_data_t;book_curs_var_b SYS_REFCURSOR-使用系统的类型 any 数据类型,动态指定任何类型,包括 anytype,anydata,anydataset 自定义数据类型 申明变量申明变量 name constantdatatypenot null:=value|default expression;子类型子类型(subtype)在 oracle 中允许自定义已经存在类型的子类型或类型的别名。有两种子类型:带约束的和不带约束的 带约束的如:SUBTYPE POSITIVE IS BINARY_