oracle 10g编程

一、概述
1.sql语言特点
sql语言采用集合操作方式,对数据的处理是成组进行的,而不是一条一条处理,听过使用集合操作方式,可以家加快数据的处理速度。
执行sql语句时每次只能发送并处理一条语句。如果要降低语句发送和处理次数,可以使用pl/sql
执行sql语句时,用户只需要知道其逻辑含义,而不需要关心sql语句的具体执行步骤
使用sql时,既可以采用交互方式执行,也可以嵌入到高级语言中执行。
2.语言分类
数据查询语言(select语句):用于检索数据库数据。在select语句的功能和语法最复杂,最灵活。
数据操纵语言(DML):用于改变数据库数据,包括insert,update,delete三条语句
事务控制语言(TCL):维护数据的一致性包括commit  rollback savepoint三条语句。
数据定义语言(DDL):用于建立、修改和删除.数据库对象。例如CREATE TABLE , ALTER TABLE  .DROP TABLE DDL会自动提交事务。
数据控制语言(DCL):用于执行权限授予和收回操作。包括GRANT好REVOKE两条命令。
3.sql语句编写规则
sql关键字不区分大小写,对象名和列名不区分大小写,字符值和日期区分大小写,如果较短则写在一行,如果较多则分布多行提高可读性。
1.2 PL/SQL简介
1.提高应用程序的运行性能
2.提供模块化的程序设计功能
3.允许定义标识符
4.具有过程语言控制结构
三、PL/SQL基础
3.1.1  PL/SQL块结构
PL/SQL块由三个部分组成:定义部分,执行部分,例外处理部分。
定义部分:定义常量,变量,游标,例外,复杂数据类型。
执行部分:实现用于模块功能,包含要执行的PL/SQL语句和sql语句。
例外处理部分:处理执行部分可能出现的运行错误。
DECLARE
/*
*定义部分--定义常量,变量,复杂数据类型,游标,例解
*/
BEGIN
/*
*执行部分--PL/SQL语句和SQL语句
*/
EXCEPTION
/*
*例外处理部分--处理运行错误
*/
END; /*块结束标志*/
 
定义部分以DECALRE开始,该部分是可选的;执行部分以BEGIN开始,该部分是必须的;例外部分以EXCEPTION开始,该部分是可选的。而END则是PL/SQL块的结束标记。注意DECALRE  BEGIN  EXCEPTION后面没有分号,而END后面必须有分号。
示例一:只包含执行部分的PL/SQL块
其中dbms_output是oracle所提供的系统包,put_line是该包所包含的过程。在使用dbms_output包输出数据或消息时,必须将sqlplus的环境变量serveroutput设置为on.
示例二:包含定义部分和执行部分额的PL/SQL块
 
示例三:包含定义部分,执行部分和例外处理部分的PL/SQL块
3.1.2 PL/SQL块分类
可以分为匿名块,命名块子程序和触发器等四种类型。
1.匿名块:没有名称的PL/SQL块,可以内嵌到应用程序也可以在交互式环境中直接使用。
该块直接以declare开始,没有名称所有叫做匿名块。
2.命名块:具有特定名称的PL/SQL块,与匿名块非常相似,只不过在PL/SQL快前使用《《》》加以标记,为了区分多层嵌套关系。
3.子程序:子程序包括过程,函数和包。当开发PL/SQL子程序时,既可以开发客户端的子程序,也可以开发服务端的子程序。客户端程序主要用在developer中,而服务器子程序可以用在任何应用程序中。通过将商业逻辑额企业规则集成到PL/SQL子程序中,可以简化客户端程序的开发和维护,并且提高应用程序的性能
1)过程:执行特定操作。当建立过程时既可以指定输入参数(in),也可以指定输出参数(out)
通过在过程中使用输入参数,可以将应用环境的数据传递到执行部分;通过使用输出参数,可以将执行部分的数据传递到应用环境。
执行的时候可以使用exec update_sal('scott',2000)
                call update_sal('scott',2000);
2)函数
函数用于返回特定数据。当建立函数时,在函数头部必须包含return子句,而在函数体内必须要包含return语句返回数据。
3)包
包用于逻辑组合相关的过程和函数,它由包规范和包体两部分组成。包规范用于定义公用的常量,变量,过程和函数。
包规范只包含了过程和函数的说明,而没有过程和函数的实现代码。包体用于实现包规范中的过程和函数
在sqlplus中建立包体可以使用CREATE PACKAGE BODY命令如下:

当调用包的过程和函数时,在过程和函数名之前必须要带有包名作为前缀,而如果要访问其他方案的包,还必须要加方案名作为前缀
4.触发器:隐含执行的存储过程。当定义触发器时,必须要指定触发事件以及触发操作,常用的触发事件包括INSERT  UPDATE DELETE,而触发器实际上是一个PL/SQL块
3.2 定义并使用变量
编写PL/SQL程序时,若临时存储数值,必须要定义常量和变量;若在应用环境和字程序之间传递数据,必须要为子程序指定参数。而在PL/SQL程序中定义变量,常量和参数时,则必须要为他们指定PL/SQL数据 类型。在编写时,可以使用标量(scalar)类型,复合(composite)类型,参照(reference)类型和LOB(Large Object)类型等四种类型
3.2.1 标量变量
只能存放单个数值的变量。当编写PL/SQL程序时,最常用的变量就是标量变量、当定义标量变量时,必须要指定标量数据类型。标量数据类型包括数字类型,字符类型日期类型和布尔类型,每种类型又包含有相应的子类型,例如NUMBER类型包含有INTGER  POSITIVE等子类型。
1.常用变量类型
1)varchar2(n)
用于定义可变长度的字符串,其中n用于指定字符串的最大长度,其最大为32767字节。当使用该数据类型定义变量时,必须要指定长度
2)char(n)
该数据类型用于定义固定长度的字符串,其中n用于指定字符串的最大长度,其最大值为32767字节。当使用该类型定义变量时,如果没有指定n则其默认值为1
3)number(p,s)
固定长度的整数和浮点数
4)date
定义日期和时间数据
5)timestamp
6)long和long raw
long数据类型用于定义变长字符串,类似于varchar2数据类型
7)Boolean
布尔变量值为true  false  null
8)binary_integer
定义整数-2147483647-2147483647之间
9)binary_float   binary_double
2.定义标量变量
编写PL/SQL程序时,如果要引用变量变量,必须首先在定义部分变量变量。然后才能在部分或例外处理部分中使用这些标量变量。
1)语法:identifier [constant] datatype [not null][:=|DEFAULT expr]
2)使用标量变量
为了降低PL/SQL程序的维护工作量,可以使用%TYPE属性定义变量。
4.使用%TYPE属性
当使用PL/SQL变量存放列值时,必须确保变量使用何时的数据类型和长度。否则在运行过程中会出现PL/SQL运行错误。为了避免这种不必要的错误,可以使用%TYPE属性定义变量。当使用%TYPE属性定义变量时,他会按照数据列或变量来确定新变量的类型的长度
3.2.2  复合变量
存放多个值的变量,当定义复合变量时,必须要使用PL/SQL的符合数据类型。PL/SQL包括PL/SQL记录,PL/SQL表,嵌套表以及VARRAY等四种符合数据类型。
1.PL/SQL记录
每个PL/SQL记录一般都包含多个成员,当使用PL/SQL记录时,首先要在定义部分记录类型和记录变量,然后在执行部分引用该记录变量,当引用记录成员时,必须要加记录变量作为前缀(记录变量.记录成员)
2.PL/SQL表
类似于高级语言中的数组。但是PL/SQL中的下标可以为负,且个数没有限制,当使用PL/SQL表时,必须首先定义部分定义PL/SQL表类型和PL/SQL表变量,然后在执行部分中引用该PL/SQL表变量。
ename_table_type是PL/SQL表类型  emp.ename%TYPE指定了PL/SQL表元素的数据类型和长度  ename_table为PL/SQL表变量,ename_table(-1)则表示下标为-1的元素。
3.嵌套表
类似于高级语言中的数字。下标都不能为负值,嵌套表的个数没有限制。和PL/SQL表非常相似,但嵌套表可以作为表里的数据类,而PL/SQL表不能作为表列的数据类型。当在表列中使用嵌套表时,必须首先使用create type语句建立嵌套表类型
emp_type是存储雇员信息,而emp_array是基于emp_type的嵌套表类型,他可以存储多个雇员的信息。当建立了嵌套表类型之后,就可以在表列或对象属性中将其最为用户自定义数据类型来应用。但是在使用嵌套表类型作为表列时必须要为其指定专门的存储表。
其中的employee是存储表。
4.varray
边长数组类似于嵌套表,他可以作为表列和对象类型属性的数据类型,但是var燃油的元素个数是由限制的。当使用varray类型时,必须首先建立varray类型
对象类型article_type用于存储文章信息,而article_array则用于存储多篇文章的信息,并且最多可以存储20篇文章。当建立varray类型之后,可以在表列或对象属性中将其最为用户自定义数据类型来引用。
嵌套表数据需要存储在专门的存储表中,而varray数据则与其他列数据一起存放在表段中。
3.2.3参照变量
参照变量指用于存放数值指针的变量。通过使用参照变量,可以使得应用程序共享相同对象,从而降低占用空间。在编写PL/SQL程序时,可以使用游标变量(ref cursor)和对象类型变量ref  obj_type等两种参照变量类型。
1.REF CURSOR
当使用显示游标时,需要在定义显示游标时指定相应的select语句,这种显示游标称为静态游标。当使用游标变量时,在定义游标变量时不需要指定select语句,而是在打开游标时指定select语句,从而实现动态的游标操作。
c1为ref cursor类型,而emp_cursor为游标变量,并且在打开游标变量时指定了其对应的select语句。
2.ref obj_type
当编写对象类型应用时,为了共享相同对象,可以使用ref引用对象类型,ref实际是指向对象实例的指针。首先建立对象类型home和对象表homes,然后插入数据。
对象表homes存放着家庭所在地以及户主姓名,假定每个家庭有四口人,当进行人口统计时,为了使得同一家庭成员可以共享家庭地址,可以使用ref引用home_type对象类型,从而降低占用空间。
3.2.4 LOB变量
LOB变量是指用于存储大批量数据的变量,oracle将lob分为两种:内部lob和外部LOB。内部LOB包括CLOB BLOB NCLOB,他们的数据被存储在数据库中,并且支持事务操作(提交 回退 保存点)。外部只有BFILE一种类型,该类型的文件被存储在OS文件中,并且不支持事务操作。其中CLOB/NCLOB用于存储大批量字符数据。BLOB用于存储大批量二进制数据,而BFILE则存储指向OS文件的指针。
3.2.5 非PL/SQL变量
当在sqlplus或应用程序中与PL/SQL块之间进行数据交互时,需要使用sqlplus变量或应用程序变量来完成。当在PL/SQL块中引用非PL/SQL变量时,必须要在非PL/SQL变量前面加冒号“:”。
1.使用sqlplus变量
在plsql块中引用sqlplus变量时,必须首先使用variable命令定义变量,而如果要在sqlplus中输出变量内容,则需要使用print命令:
3.3 编写PL/SQL代码
在编写PL/SQL应用程序时,为了开发正确,高效的PL/SQL块,必须要遵从PL/SQL代码编写的规则,否则会导致编译错误或运行错误。而每行PL/SQL代码又是特定的PL/SQL词汇单元组成的,所以PL/SQL应用开发人员处理要遵从代码编写规则之外,还必须要掌握PL/SQL各种词汇单元的作用。
3.3.1 PL/SQL词汇单元
当编写PL/SQL块时,每个PL/SQL块都包含多行代码,而每行代码又是由多个合法单元组成的,这些合法单元被称为词汇
DECLARE
  V_SAL NUMBER(6,2);
BEGIN
  V_SAL:=1000;
END;
/
PL/SQL有分隔符,标识符,文字串和注释等四种词汇单元。
1.分隔符
具有特定含义的单个符号和组合符号
单符号分隔符(+ % ‘  .  / ( ) : , * " = < > @ ; -)
组合分隔符(:=   ->  ||   **   <<    >>   /*    */    ..   <>   !=  ^=  <=   >= --)
2.标识符
指定PL/SQL程序单元和程序项 的名称。通过使用标识符,可以定义常量,变量,例外,显示游标,游标变量,参数,子程序以及包的名称。
规则:每行只能定义一个标识符
          标识符以阿拉伯字符开始最大为30个,如果以其他字符开始,必须使用双引号引主
         只能用A-Z a-z  0-9  _ $ #其他要用双引号引住
        不能使用oracle关键字
3.文本
文本是指数字,字符,字符串,日期值或布尔值,而不是标识符。文本包括数字文本,字符文本,字符串文本,布尔文本,日期时间文本。
3.3.2 PL/SQL代码编写规则
1.标识符命名规则
定义变量时,建议使用v_做为前缀
定义常量时,建议使用c_做为前缀
定义游标时,使用_cursor作为后缀
定义例外时,使用e_作为前缀
定义表类型时,使用_table_type作为后缀
定义表变量时,使用_table最为后缀
定义记录类型时,使用_record_type作为后缀
定义记录变量时,使用_record最为后缀
2.大小写规则
SQL关键字采用大写格式
PL/SQL关键字采用大写格式
数据类型采用大写格式
标识符和参数采用小写格式
数据库对象和列采用小写格式。
3.代码缩进
4.嵌套块和变量范围
嵌套块是指嵌入在一个PL/SQL块中的另一个PL/SQL块,其中被嵌入的块被称为子块,而包含字块的PL/SQL块则被称为主块。当使用嵌套块时,字块可以引用主块所定义的任何标识符,但主块却不能引用字块的任何标识符。即字块定义的标识符是局部标识符,而主块定义的标识符是全局标识符。
5.在PL/SQL块中可以使用的SQL函数
当编写PL/SQL代码时,可以直接引用大多数的单行SQL函数。包括单行数字函数ROUND  单行字符函数UPPER   转换函数TO_CHAR  日期函数MONTHS_BETWEEN
四  使用SQL语句
4.1 使用基本查询
1,确定表结构
desc  table
2.
一、使用ALL操作符执行多表插入
INSERT ALL
WHEN deptno=10 THEN INTO dept10
WHEN deptno=20 THEN INTO dept20
WHEN deptno=30 THEN INTO dept30
WHEN job='CLERK' THEN INTO clerk
ELSE INTO other
SELECT * FROM emp;
结构是将部门10的雇员信息插入到DEPT10表,将部门10的雇员信息插入到dept20中,将部门30的雇员信息插入到dept30中,将岗位clerk的所有雇员插入到clerk,将其他插入到other表。
二、使用FIRST操作符执行
INSERT INTO 
WHEN deptno=10 THEN INTO dept10
WHEN deptno=20 THEN INTO dept20
WHEN deptno=30 THEN INTO dept30
WHEN job='CLERK' THEN INTO clerk
ELSE INTO other.
使用first操作符执行操作时,如果数据以及满足了先前条件,并且已经被插入到某表,那么该行数据在后续插入中将不会被再次使用
更新
使用子查询更新数据:
UPDATE emp SET (job,sal,comm)=(SELECT job,sal,comm FROM emp WHERE ename='SMITH') WHERE ename='SCOTT';
复制表数据:
UPDATE employee SET deptno=(SELECT deptno FROM emp WHERE empno=7788) WHERE job=(SELECT job FROM emp WHERE empno=7788);
删除数据:
delete删除数据不会释放表段所占用的空间,truncate删除会释放所占用的空间,delete语句de 操作可以回退,但是truncate语句的操作不可以回退。
4.3  使用事务控制语句
事务用于确保数据库数据的一致性,他由一组相关的DML语句组成。该组DML语句所执行的操作要么全部成功要么全部取消。
数据库事务主要由INSERT UPDATE  DELETE 和SELECT ... FOR UPDATE语句组成。当在应用程序中执行第一条SQL语句时开始事务,当执行COMMIT 或ROLLBACK语句时结束事务。
分组函数:
ROLLUP:生成横向的统计结果
SELECT deptno,job ,avg(sal) FROM emp 
GROUP BY ROLLUP (deptno,job);
CUBE:生成纵向统计结果
SELECT deptno,job,avg(sal) FROM emp 
GROUP BY CUBE(deptno,job);
GROUPING:确定统计结果是否用到了特定列,0表示使用了,1表示没有使用
SELECT deptno,job,avg(sal) ,grouping(deptno) ,grouping(job) 
FROM emp GROUP BY cube(deptno,job);
GROUPING SETS:生成多种分组数据结构
--显示部门平均工资
SELECT deptno,avg(sal) FROM emp GROUP BY deptno;
--显示岗位平均工资
SELECT job,avg(sal) FROM emp GROUP BY job;
--显示部门平均工资和岗位平均工资
SELECT job,avg(sal) FROM emp GROUP BY GROUPING SETS(deptno,job);
 
队列子查询:
SELECT ename,job,sal ,deptno FROM emp WHERE (deptno,job)=(SELECTT deptno,job FROM emp  WHERE ename='SMITH');
 
 
成对比较示例:
SELECT ename ,sal ,comm ,deptno FROM emp WHERE (sal,nvl(comm,-1)) IN(SELECT sal,nvl(comm,-1) FROM emp WHERE deptno=30);
非成对比较示例:
SELECT ename,sal ,comm ,deptno FROM emp WHERE sal IN (SELECT sal FROM  emp WHEE deptno30)  AND nvl(comm ,-1 ) IN (SELECT nvl(comm,-1) FROM emp WHERE deptno=30);
 
相关子查询:需要引用主查询表列的子查询语句,相关子查询是通过EXISTS谓词来实现的
SELECT ename,job,sal,deptno FROM emp WHERE EXISTS (SELECT 11 FROM dept WHERE dept.deptno=emp.deptno AND dept.loc='NEW YORK');
在FROM子句中使用子查询:该子查询会被视为视图对待,因此也被视为内嵌视图,在FROM子句中使用子查询时,必须给子查询指定列名。
SELECT ename,job,sal FROM emp ,(SELECT depto,avg(sal) avgsal FROM  emp GROUP BY deptno )dept WHERE emp.deptno=dept.deptno AND sal>dept.avgsal
 
在DML语句中使用子查询:
在INSERT语句中使用子查询:
INSERT INTO employee(id,name,title,salary) AELECT empno,ename ,job ,sal FROM emp;
在UPDATE 语句中使用子查询
UPDATE emp SET(sal.comm)=(SELECT sal,comm FROM emp WHERE ename='SMITH') WHERE job=(SELECT job FROM emp WHERE ename='SMITH');
在DELETE 语句中使用子查询:
DELETE FROM emp WHERE deptno=(SELECT deptno FROM dept WHERE dname='SALES')
 
 
在DDL语句中使用子查询
在CREATE TABLE语句中使用子查询
CREATE TABLE new_emp(id,name,sal,deptno) AS SELECT empno,ename ,sal,job ,deptno FROM mep;
在CREATE VIEW语句中使用子查询
CREATE OR REPLACE VIEW dept_10 AS SELECT empno,ename,job,sal ,deptno FROM emp WHERE deptno=10 ORDER BY empno;
在CREATE MATERIALIZED VIEW语句中使用子查询
CREATE MATERIALIZED VIEW summary_emp AS SELECT deptno,job,avg(sal) avgsal ,sum(sal) sumsal FROM emp ORDER BY cube(deptno,job);
 
合并查询:
UNION: 并集取消重复行
SELECT ename,sal,job FROM emp WHERE sal>2500
UNION
SELECT ename,sal,job FROM emp WHERE job='MANAGER';
UNION ALL: 并集 不取消重复行
SELECT ename,sal,job FROM emp WHERE sal>2500
UNION ALL
SELECT ename,sal,job FROM emp WHERE job='MANAGER';
INTERSECT:交集
SELECT ename,sal,job FROM emp WHERE sal>2500
INTERSECT 
SELECT ename ,sal,job FROM emp WHERE job='MANAGE';
MINUS:差集
SELECT ename,sal,job FROM emp WHERE sal>2500
MINUS
SELECT ename ,sal,job FROM emp WHERE job='MANAGE';
4.8其他复杂查询
1.层次查询
START WITH:指定层次查询的根行
CONNECT BY:指定父行和子行之间的关系。在condition表达式中,必须使用PRIOR引用父行
... PRIOR expr=expr 或... expr =PRIOR expr
例:emp表具有层次结构数据的表,其中empno列对应于雇员号,而MGR列对应于管理者编号通过使用层次查询,可以显示雇员之间的上下级关系。例为显示除CLERK外所有其他雇员的上下级关系
SELECT LPAD(' ',3*(LEVEL-1))||ename ename,
LPAD(' ',3*(level-1))||job job FROM emp
WHERE job<>'CLERK' START WITH mgr is NULL 
CONNECT BY mgr=PRIOR empno
2.使用CASE表达式
SELECT ename,sal ,CASE WHEN sal>3000 THEN 3
WHEN sal>2000 THEN 2 ELSE 1 END grade
FROM emp WHERE deptno=10;
3.倒叙查询
查看当前数据
SELECT ename,sal FROM emp WHERE ename='CLERK'
查看历史数据
SELECT ename,sal FROM emp AS OF TIMESTAMP to_timestamp('2003-05-18 19:59:00','yyyy-mm-dd hh24:mi:ss')
WHERE ename='CLARK'
使用DBMS_FLASHBACK包获取特定SCN的数据
exec dbms_flashback.enable_at_system_change_number(717402)
select sal fom emp where ename='CLERK'
exec dbms_flashback.disable
select sal from emp where ename='SCOTT';
4.使用WITH子句重用子查询
两次使用子查询:
SELECT dname,SUM(sal) AS dept_total FROM emp,dept
WHERE emp.deptno=dept.deptno GROUP BY dname
HAVING SUM(sal)>
(SELECT SUM(sal)*1/3 FROM emp,dept 
WHERE emp.deptno=dept.deptno);
使用WITH子句重用子查询:
WITH summary AS (
SELECT dname,SUM(sal) AS dept_total FROM emp ,dept 
WHERE emp.deptno=dept.deptno FROUP BY dname
)
SELECT dname,dept_total FROM summary WHERE dept_total>
(SELECT SUM(dept_total)*1/3 FROM summary);
 
五:SQL函数
FIRST:与其他分组函数结合使用,通过使用该函数,可以取得排序等级的第一级,然后使用分组函数汇总该等级的数据。
SELECT MIN(sal) KEEP (DENSE_RANK FIRST ORDER BY comm DESC) "补助最高级别雇员的最低工资",
MAX(sal) KEEP(DENSE_RANK FIRST ORDER BY comm DESC) "补助最高级别雇员的最高工资" FROM emp;
 
LAST:与其他分组函数结合使用,可以取得排序登机的最后一级,然后使用分组函数汇总该等级的数据。
SELECT MIN(sal) KEEP (DENSE_RANK LAST ORDER BY comm) "补助最高级别雇员的最低工资", MAX(sal) KEEP (DENSE_RANK LAST ORDER BY comm) "补助最高级别雇员的最高工资" FROM emp;
 
六 :访问ORACLE
6.1 检索单行数据
通过在PL/SQL块中嵌入SELECT语句,可以将数据库数据检索到变量中,然后可以输出或处理该变量的数据。在PL/SQL块中嵌入SELECT 语句时,必须要带有INTO子句:
SELECT select_list INTO (variable_name) FROM table WHERE condition;
1.使用标量变量接收数据
2.使用记录变量接收数据
使用记录变量接收数据时,记录成员的个数必须要与选择列表项的个数完全一致,并且记录成员的类型要与长度等也要相匹配。
3.嵌入SELECT 语句注意事项
当在PL/SQL块中直接使用SELECT INTO语句时,该语句必须返回一条数据,并且只能返回一条数据,否则会触发PL/SQL例外,或显示错误信息。
1)NO_DATA_FOUND
2)TOO_MANY_ROWS
3)WHERE子句使用注意事项
在where子句中使用变量时注意,变量名不能与列名相同,否则会触发TO_MANG_ROWS例外。
6.2.4  SQL游标
在oracle中,游标包括隐含游标和显式游标两种类型。其中隐含游标又被称为SQL游标,他专门用于处理SELECT INTO,INSERT ,UPDATE以及DELETE语句,而显示游标则用于处理多行的SELECT语句。当在PL/SQL块中执行INSERT,UPDATE以及DELETE语句时,为了取得DML语句作用的结果,必须使用 SQL游标属性,SQL游标包括SQL%FOUND  SQL%NOTFOUND  SQL%ROWCOUNT  SQL%ISOPEN等四种属性。
1.SQL%ISOPEN
游标属性SQL%ISOPEN用于确定SQL游标是否已经打开。当在PL/SQL块中执行SELECT INTO,INSERT,UPDATE以及DELETE语句时,oracle会隐含地打开游标,并且在语句执行完成后会隐含地关闭游标所以对于开发人员来说该属性的值永远都是FALSE,并且在挨罚PL/SQL应用时不需要使用该游标属性。
2.SQL%FOUND
确定SQL语句是否执行成功
 
3.SQL%NOTFOUND
同上,只是真值相反
4.SQL%ROWCOUNT
返回SQL语句所作用的总计行数
6.3 事务控制语句
示例一:在PL/SQL块中使用COMMIT和ROLLBACK语句
示例二:在PL/SQL块中使用OLLBACK和SAVEPOINT语句
SAVEPOINT语句用于设置保存点,通过与ROLLBACK语句结合可以取消部分事务。
 
七:编写控制结构
IF-THEN    IF-THEN-ELSE    IF-THEN-ELSIF
1.简单条件判断
2.二重条件分支
3.多重条件分支
7.2 CASE语句
1.在CASE语句中使用单一选择符金秀贤等值比较
2.在CASE语句中使用多种条件比较
7.3 循环语句
1.基本循环
2.WHILE循环
3.FOR循环
FOR counter in [REVERSE]
  lower_bound..upper_bound LOOP
   statement
  ...
END LOOP;
其中counter是循环控制变量,不需要显式定义;lower_bound和upper_bound是对应于循环控制变量的下界和上界。默认循环时,循环变量会随着循环每次增一,如果指定REVERSE选项,那么每次循环时循环变量会自动减一
4.嵌套循环和标号
7.4 顺序控制语句
1.GOTO:一般不使用,会使得可读性变差
2.NULL:NULL语句不会执行任何操作,并且会传递到下一条语句,主要是可以提高可读性。
八:使用复合数据类型
8.1 PL/SQL记录
8.1.1 定义PL/SQL记录
2.使用%ROWTYPE属性定义记录变量
%ROWTYPW属性可以基于表或视图定义记录变量。当使用该属性定义记录变量时,记录成员的名称和类型与表或视图的名称和类型一样
identifier table_name%ROWTYPE;
identifier view_name%ROWTYPE;
8.1.2 使用PL/SQL记录
1.在SELECT INTO语句中使用PL/SQL记录
示例一:在SELECT INTO 语句中使用记录变量
示例二:在SELECT INTO 语句中使用记录成员
 
2.在INSERT INTO语句中使用PL/SQL记录
1).在values子句中使用记录变量。
DECLARE
  dept_record dept%ROWTYPW;
BEGIN
  dept_record.deptno:=50;
  dept_record.dname:='ADMINISTRATOR';
  dept_record.loc:='BEIJING';
  INSERT INTO dept VALUES dept_record;
END;
/
2).在values子句中使用记录变量
DECLARE
  dept_record dept%ROWTYPW;
BEGIN
  dept_record.deptno:=60;
  dept_record.dname:='sales';
  INSERT INTO dept (deptno,dname)VALUES (dept_record.deptno,dept_record.dname);
END;
/
3、在UPDATE语句中使用PL/SQL记录
1)在set子句中使用记录变量
DECLARE
  dept_record dept%ROWTYPW;
BEGIN
  dept_record.deptno:=30;
  dept_record.dname:='sales';
  dept_record.loc:='SHANGHAI';
  UPDATE dept SET ROW=dept_record WHERE deptno=30;
 END;
/
2)在set子句中使用记录成员
DECLARE
  dept_record dept%ROWTYPW;
BEGIN
  dept_record.loc:='GUANGZHOU';
  UPDATE dept SET loc=dept_record.loc WHERE deptno=10;
 END;
/
4、在delete语句中使用PL/SQL记录
DECLARE
   dept_record  dept%ROWTYPE;
BEGIN
    dept_record.deptno:=50;
    DELETE FROM dept WHERE deptno=dept_record.deptno;
END;
/
8.2 PL/SQL集合
8.2.1  索引表
处理PL/SQL数组的数据类型,个数无限制,且下标可为负
TYPE type_name is TABLE OF element_type
[NOT NULL] INDEX BY key_type;
identifier type_name;
示例一:在索引中使用BINARY_INTEGER和PLS_INTEGER
示例二:在索引中使用VARCHAR2
8.2.2 嵌套表
TYPE type_name IS TABLE OF element_type;
identifier type_name;
1.在PL/SQL块中使用嵌套表
2.在表列中使用嵌套表
CREATE TYPE phone_type IS TABLE OF VARCHAR2(20);
/
CREATE TABLE employee(
id NUMBER(4),NAME VARCHAR2(10),SAL NUMBER(6,2), PHONE PHONE_TYPE
)NETSTAT TABLE PHONE STORE AS PHONE_TABLE;
示例一:在PL/SQL块中为嵌套表列插入数据
BEGIN
  INSERT INTO employee VALUES(1,'scott',800,phone_type('','041-345678','13804711111'));
END;
/
示例二:在PL/SQL块中检索嵌套表列的数据
DECLARE
  phone_table  phone_type;
BEGIN
  SELECT phone INTO phone_table FROM employee WHERE id=1;
FOR i IN 1..phone_table.count loop
 dbms_output.put_line(phone_table(i));
END LOOP;
END;
/
示例三:在PL/SQL块中更新嵌套表列的数据
DECLARE
  phone_table  phone_type:=phone_type('2','3','4','5');
BEGIN 
   UPDATE employee SET phone=phone_table WHERE id=1;
END;
/
8.2.3 变长数组
示例一:在PL/SQL块中使用VARRY
DECLARE
  TYPE ename table_type IS VARRY(20) OF emp_ename%TYPE;
  ename_table ename_table_type:=ename_table_type('mary');
BEGIN
  SELECT ename INTO ename_table(1) FROM emp WHERE empno=&no;
dbms_output.put_line(ename_table(1));
end;
/
示例二:在表列中使用VARRAY
CREATE TYPE phone_type IS VARRAY(20) of VARCHAR2(20);
/
CREATE TABLE employee(
id NUMBER(4) ,name VARCHAR2(20),sal NUMBER(6,2),phone phone_type
);
8.2.6 集合方法
cellection_name.method_name[parameters];
其中EXISTS  COUNT  LIMIT   FIRST   NEXT  PRIOR  NEXT是函数 EXTEND TRIM  DELETE是过程。
1.EXISTS
确定集合元素是否存在,如果存在,返回true,否则返回false
DECLARE
  TYPE ename_table_type IS TABLE OF emp.ename%TYPE;
  ename_table ename_table_type;
BEGIN
  IF ename_table.EXISTS(1) THEN
   ename_table(1):='SCOTT';
  ELSE
    dbms_output.put_line('必须初始化集合元素');
  END IF;
END;
/
2.COUNT
返回当前集合中的元素总个数
DECLARE
   TYPE  ename_table_type IS TABLE OF emp.ename%type
   INDEX BY BINARY_INTEGER;
  ename table ename_table_type;
BEGIN
   ename_table(-5):='SCOTT';
   ename_table(1):='SMITH';
   ename_table(5):='MARY';
   ename_table(10):='BLAKE';
   dbms_output.put_line(ename_table.count);
END;
/
3.LIMIT
返回集合元素的最大个数。
DECLARE
   TYPE ename_table_type IS VARRAY(20) OF emp.ename%TYPE;
   ename_table ename_table_type:=ename_table_type('mary');
BEGIN
   dbms_output.put_line(ename_table.limit);
END;
/
4.FIRST和LAST
DECLARE
   TYPE  ename_table_type IS TABLE OF emp.ename%type
   INDEX BY BINARY_INTEGER;
  ename table ename_table_type;
BEGIN
   ename_table(-5):='SCOTT';
   ename_table(1):='SMITH';
   ename_table(5):='MARY';
   ename_table(10):='BLAKE';
   dbms_output.put_line(ename_table.FIRST);
   dbms_output.put_line(ename_table.LAST);
END;
/
5.PRIOR和NEXT
DECLARE
   TYPE  ename_table_type IS TABLE OF emp.ename%type
   INDEX BY BINARY_INTEGER;
  ename table ename_table_type;
BEGIN
   ename_table(-5):='SCOTT';
   ename_table(1):='SMITH';
   ename_table(5):='MARY';
   ename_table(10):='BLAKE';
   dbms_output.put_line(ename_table.pprior(5));
   dbms_output.put_line(ename_table.next(5));
END;
/
6.EXTEND
DECLARE
   TYPE  ename_table_type IS TABLE OF emp.ename%type
   INDEX BY BINARY_INTEGER;
  ename table ename_table_type;
BEGIN
   ename_table:=ename_table_type('mary');
   ename_table.extend(5,1);
   dbms_output.put_line(ename_table.COUNT);
END;
/
7.TRIM和TRIM(n)
DECLARE
   TYPE ename_table_type IS TABLE OF VARCHAR2(10);
   ename_table ename_table_type;
BEGIN
   ename_table:=ename_table_type('A','A','A','A','A');
   ename_table.trim(2);
   dbms_output.put_line(ename_table.count);
END;
/
8.DELETE
DECLARE
   TYPE  ename_table_type IS TABLE OF emp.ename%type
   INDEX BY BINARY_INTEGER;
  ename table ename_table_type;
BEGIN
   ename_table(-5):='SCOTT';
   ename_table(1):='SMITH';
   ename_table(5):='MARY';
   ename_table(10):='BLAKE';
   ename_table.DELETE(5);
   dbms_output.put_line(ename_table.count);
END;
/
8.3 批量绑定
1.不使用批量绑定
DECLARE
   TYPE id_table_type IS TABLE OF NUMBER(6)
    INDEX BY BINARY_INTEGER;
   TYPE  name_table_type IS TABLE OF VARCHAR2(10)
    INDEX BY BINARY_INTEGER;
   id_table  id_table_type;
   name_table  name_table_type;
   start_time NUMBER(10);
   end_time  NUMBER(10);
BEGIN
  FOR i IN 1..5000 LOOP
   id_table(i):=i;
   name_table(i):='name'||to_char(i);
  END LOOP;
  start_time:=dbma_utility.get_time;
  FOR i IN 1..id_table.COUNT LOOP
     INSERT INTO demp VALUES(id_table(i),name_table(i));
  END LOOP;
  end_time:=dbms_utility.get_time;
  dbms_output.put_line((end_time-start_time)/100);
END;
/
2.使用变量绑定
DECLARE
   TYPE id_table_type IS TABLE OF NUMBER(6)
    INDEX BY BINARY_INTEGER;
   TYPE  name_table_type IS TABLE OF VARCHAR2(10)
    INDEX BY BINARY_INTEGER;
   id_table  id_table_type;
   name_table  name_table_type;
   start_time NUMBER(10);
   end_time  NUMBER(10);
BEGIN
  FOR i IN 1..5000 LOOP
   id_table(i):=i;
   name_table(i):='name'||to_char(i);
  END LOOP;
  start_time:=dbma_utility.get_time;
  FORALL i IN 1..id_table.COUNT LOOP
     INSERT INTO demp VALUES(id_table(i),name_table(i));
  end_time:=dbms_utility.get_time;
  dbms_output.put_line((end_time-start_time)/100);
END;
/
3.结论
批量绑定是使用BULK COLLECTION子句和FORALL语句来完成的。其中BULK COLLECT子句用于取得批量数据,该子句只能用于SELECT语句,FETCH语句和DML返回子句;而FORALL语句只适应与执行批量的DML操作。
8.3.1 FORALL语句
在PL/SQL中执行批量INSERT UPDATE 和DELETE操作时,可以使用FORALL语句。在oracle10G开始使用增加的INDICES OF子句和VALUES OF子句,可以使用不连续的集合元素。注意FOR语句是循环语句,但FORALL语句却不是循环语句
语法一:
 FORALL index lower_bound..upper_bound
      sql_statement;
index是隐含定义的整数变量,lower_bound和upper_bound分别是集合的上届和下界
语法二:
FORALL index IN INDICES OF collection 
          [BETWEEN lower_bound.AND  upper_bound]
          sql_statement;
INDICES OF子句用于指定只取得对应于collection集合元素下标的index值。
语法三
FORALL index IN VALUE OF index_collection
    sql_statement;
1.在INSERT语句上使用批量绑定
DECLARE
   TYPE id_table_type IS TABLE OF NUMBER(6)
    INDEX BY BINARY_INTEGER;
   TYPE  name_table_type IS TABLE OF VARCHAR2(10)
    INDEX BY BINARY_INTEGER;
   id_table  id_table_type;
   name_table  name_table_type;
   start_time NUMBER(10);
   end_time  NUMBER(10);
BEGIN
  FOR i IN 1..10 LOOP
   id_table(i):=i;
   name_table(i):='name'||to_char(i);
  END LOOP;
  FORALL i IN 1..id_table.COUNT LOOP
     INSERT INTO demp VALUES(id_table(i),name_table(i));
END;
/
2.在update语句上使用批量绑定
 
DECLARE
   TYPE id_table_type IS TABLE OF NUMBER(6)
    INDEX BY BINARY_INTEGER;
   TYPE  name_table_type IS TABLE OF VARCHAR2(10)
    INDEX BY BINARY_INTEGER;
   id_table  id_table_type;
   name_table  name_table_type;
BEGIN
  FOR i IN 1..5 LOOP
   id_table(i):=i;
   name_table(i):='N'||to_char(i);
  END LOOP;
  FORALL i IN 1..id_table.COUNT LOOP
    update demo set name=name_table(i) where id=id_table(i);
END;
/
3.在delete语句上使用批量绑定
DECLARE
  TYPE id_table_type IS TABLE OF NUMBER(6)
   INDEX BY BINARY_INTEGER;
   id_table  id_table_type;
BEGIN
  FOR i  IN 1..3 LOOP
    id_table(i):=i;
END LOOP;
FORALL i IN 1..ID_TABLE.COUNT
   DELETE FROM demo  WHERE id=id_table(i);
END;
/
4.在FORALL语句中使用部分集合元素
DECLARE
  TYPE  id_table_type  IS TABLE OF NUMBER(6)
    INDEX BY BINARY_INTEGER;
    id_table id_table_type;
BEGIN
   FOR  i  IN 1..10  LOOP
     id_table(i):=11-i;
END LOOP;
FORALL i  IN 8..10
   INSERT INTO demo(id) VALUES(id_table(i));
END;
/
8.3.2   BULK COLLECT子句
BULK COLLECT子句用于取得批量数据,他只适用于SELECT INTO语句,FETCH INTO语句和DML返回子句。通过使用该子句,可以将批量数据存放到PL/SQL集合变量中。
语法:.. BULK COLLECT INTO collection_name 
1.在SELECT INTO语句中使用BULK COLLECT子句
DECLARE
   TYPE  emp_table_type IS TABLE OF emp%rowtype
    INDEX BY BINARY_INTEGER;
   emp_table emp_table_type;
BEGIN
   SELECT * BULK COLLECT INTO emp_table  FROM emp WHERE deptno=&no;
FOR i IN 1..emp_table.COUNT LOOP
    dbms_output.put_line(EMP_TABLE(i).ENAME);
end loop;
end;
/
2.在DML的返回子句中使用BULK COLLECT子句
DECLARE
  TYPE  ename_table_type IS TABLE OF emp.ename%TYPE;
  ename_table  ename_table_type;
BEGIN
   DELETE FROM emp WHERE deptno=&NO;
     RETURNING ename BULK COLLECT INTO ename_table;
   dbma_output.put('雇员名:');
FOR  i IN 1..ename_table.COUNT LOOP
   dbms_output.put_line(ename_table(i)||'');
END LOOP;
DBMS_OUTPUT.NEW_LINE;
END;
/
 
九  使用游标
9.1显式游标
PL/SQL包含隐含游标和显示游标,其中隐含游标用于处理SELECT INTO和DML语句,而显式游标则专门用于处理SELECT语句返回的多行数据
1.使用显式游标
DECLARE-OPEN-FETCH-是否为空-CLOSE
1)定义游标
CURSOR cursor_name  IS select_statement;
2)打开游标
OPEN cursor_name
3)提取数据
语法一:FETCH cursor_name INTO variable1,variable2
语法二:FETCH  cursor_name BULK COLLECT INTO collect1,collect2
4)关闭游标
CLOSE  cursor_name;
3.显式游标使用示例
示例一:在显示游标中使用FETCH...INTO语句
DECLARE
  CURSOR  emp_cursor  IS
    SELECT ename,sal FROM emp WHERE deptno=10;
  v_ename emp.ename%TYPE;
  v_sal  emp.sal%TYPE;
BEGIN
  OPEN  emp_cursor;
  LOOP
   FETCH  emp_cursor INTO v_ename,v_sal;
   EXIT  WHEN emp.cursor%NOTFOUND;
   dbms_output.put_line(v_ename||':'v_sal);
 END LOOP;
CLOSE emp_cursor;
END;
/
示例二:在显示游标中,使用FETCH..BULK  COLLECT INTO语句提取所有数据
DECLARE
  CURSOR  emp_cursor  IS
   SELECT ename FROM emp WHERE deptno=10;
  TYPE  ename_table_type IS TABLE OF VARCHAR2(10);
   ename_table ename_table_type;
 BEGIN
   OPEN emp_sursor;
   FETCH emp_cursor BULK COLLECT INTO ename_table;
   FOR i IN 1..ename_table.COUNT LOOP
     dbms_output.put_line(ename_table(i));
   END LOOP;
    CLOSE emp_cursor;
  END;
/
示例三:基于游标定义记录变量
DECLARE
   CURSOR emp_cursor  IS SELECT ename,sal FROM emp;
   emp_record  emp_cursor%ROWTYPE;
BEGIN
   OPEN emp_cursor;
LOOP
  FETCH  emp_cursor  INTO emp_record;
  EXIT WHEN  emp_cursor%NOTFOUND;
  dbms_output.put_line(emp_record.ename||':'emp_record.sal);
END LOOP;
CLOSE emp_cursor;
END;
/
9.2参数游标
CURSOR cursor_name (parameter_name  datatype) IS select_statement;
其中参数只能指定数据类型,而不能指定长度。
DECLARE
   CURSOR emp_cursor(no NUMBER) IS 
    SELECT ename FROM emp WHEE deptno=no;
    v_ename emp.ename%TYPE;
 BEGIN
   OPEN emp_cursor(10);
  LOOP
   FETCH emp_cursor  INTO v_ename;
   EXIT WHEN emp_cursor%NOTFOUND;
   dbms_output.put_line(v_ename);
  END LOOP;
  close EMP_CURSOR;
END;
/
9.3使用游标更新或删除数据
CURSOR cursor_name(parameter_name datatype)
IS select_statement
FOR UPDATE [OF column_reference] [NOWAIT];
提取游标数据之后为了更新或删除当前游标数据,必须在UPDATE或DELETE语句中引用WHERE CURRENT OF子句:
UPDATE table_name  SET  column=.. WHERE CURRENT OF cursor_name;
DELETE table_name WHERE current OF cur_sor_name;
1.使用游标更新数据
DECLARE
   CURSOR  emp_cursor  IS
    SELECT ENAME,SAL FROM emp FOR UPDATE;
   v_ename emp.ename%TYPE;
   v_oldsal   emp.sal%TYPE;
BEGIN
   OPEN  emp_cursor;
LOOP
  FETCH emp_cursor INTO v_ename,v_oldsal;
  EXIT WHEN emp_cursor%NOTFOUND;
  IF  v_oldsal<2000  THEN
    UPDATE emp  SET sal=sal+100 WHERE CURRENT OF emp_cursor;
  END IF;
END LOOP;
CLOSE emp_cursor;
END;
/
2.使用游标删除数据
DECLARE
   CURSOR  emp_cursor
     SELECT ename,sal,deptno FROM emp for update;
    V_ENAME EMP.ENAME%TYPE;
    V_OLDSAL EMP.SAL%TYPE;
    V_DEPTNO  EMP.DEPTNO%TYPE;
   BEGIN
     OPEN  emp_cursor;
     LOOP
        FETCH emp_cursor  INTO v_ename,v_oldsal,v_deptno;
        EXIT WHEN emp_cursor%NOTFOUND;
        IF v_deptno=30 then
          DELETE FROM emp   WHERE CURRENT OF emp_cursor;
        END IF;
      END LOOP'
    CLOSE  emp_cursor;
END;
/
9.4  游标FOR循环
游标FOR循环是在PL/SQL块中使用最简单的方式,简化了对游标的处理。当使用游标FR循环时,oracle会隐含地打开游标,提取游标数据并关闭游标。
语法:FOR  record_name IN cursor_name LOOP
         statement1;
         statement2;
      END LOOP;
其中cursor_name是已经顶底的游标名;record_name是oracle隐含定义的记录变量名。
1.使用游标FOR循环
DECLARE
  CURSOR emp_cursor  IS SELECT ename,sal FROM emp;
BEGIN
  FOR   emp_record IN emp_cursor LOOP
    dbms_output.put_line('第'||emp_cursor%ROWCOUNT||'个雇员:'||emp_record.ename);
  END LOOP;
END;
/
2.在游标FOR循环中直接使用子查询
当使用游标FOR循环时,习惯做法是首先定义部分定义游标,然后在游标FOR循环中引用该游标。如果在使用游标FOR循环时不需要使用任何游标属性那么可直接在游标FOR循环中使用子查询。
BEGIN
   FOR   emp_record  IN
     (SELECT ename,sal FROM emp) LOOP
     dbms_output.put_line(emp_record.ename);
   END LOOP;
END;
/
9.5使用游标变量
1.游标变量使用步骤
包括定义游标变量,打开游标,提取游标数据,关闭游标等四个阶段。
1)定义REF CURSOR类型和游标变量
TYPE   ref_type_name IS  REF CURSOR [RETURN  return_type]; 
cursor_variable  ref_type_name;
其中ref_type_name是用于指定自定义类型名。return子句用于指定REF  CURSOR返回结果的数据类型。cursor_variable用于指定游标变量名。
2)打开游标
OPEN  cursor_variable  FOR select_statement;
3)提取游标数据
一:FETCH cursor_variable INTO variable1,variable2...;
二:FETCH  cursor_variablr 
     BULK  COLLECT INTO collect1,collect2...[LIMIT rows];
4)关闭游标变量
CLOSE  cursor_variable;
 
9.6 使用CURSOR表达式
用于返回嵌套游标。
CURSOR(subquery)
示例:
DECLARE
  TYPE refcursor  IS REF CURSOR;
  CURSOR dept_cursor (no number) IS
     SELECT a.dname,CURSOR(SELECT ename,sal FROM emp WHERE deptno=a.deptno) FROM dept a WHERE a.deptno=no;
  empcur   refcursor;
  v_dname dept.dname%TYPE;
  v_ename  emp.ename%TYPE;
  v_sal  emp.sal%TYPE;
BEGIN
  OPEN dept_cursor(&no);
LOOP
FETCH  dept_cursor INTO v_dname,empcur;
EXIT WHEN  dept_cursor%NOTFOUND;
dbms_output.put_line(v_dname);
LOOP
   FETCH   empcur  INTO v_ename,v_sal;
   EXIT  WHEN empcur%NOTFOUND;
dbms_output.put_line(v_ename||v_sal);
END LOOP;
END LOOP;
CLOSE dept_cursor;
END;
 
第十章  处理意外
10.1 例外简介
例外是一种PL/SQL标识符,如果允许PL/SQL块时出现错误或警告,则会触发例外。当触发例外时,默认情况下会终止PL/SQL块的执行,通过在PL/SQL块中引入例外处理部分,可以捕捉各种例外
1.例外分类
包括预定义例外,非预定义例外和自定义例外三种类型。
预定义例外  非预定义例外   自定义例外 
2.处理例外
 
10.2 处理预定义例外
1.常用预定义例外
ACCESS_INTO_NILL
CASE_NOT_FOUND
COLLECTION_IS_NULL
CURSOR_ALREADY_OPEN
DUP_VAL_ON_INDEX
INVALID_CURSOR
INVALIS_NUMBER
NO_DATA_FOUND
TOO_MANY_ROWS
ZERO_DIVIDE
SUBSCRIPT_BEROND_COUNT
SUBSCRIPT_OUTSIDE_LIMIT
VALUE_ERROR
2.其他预定义例外
LOGIN_DENIED
NOT_LOGGED_ON
PROGRAM_ERROR
ROWTYPE MISMATCH
SELF_IS_NULL
STORAGE_ERROR
SYS_INVALID_ROWID
TIMEOUT_ON_RESOURCE
10.5使用例外函数
1.SQLCODE和SQLERRM
SQLCODE用于返回ORACLE错误号而SQLERM则用于返回该错误号所随影的错误消息。
示例:
undef v_sal
DECLARE
   v_ename emp.ename%TYPE;
BEGIN
   SELECT ename INTO v_ename FROM emp WHERE sal=&&v_sal;
  dbms_output.put_line(v_ename);
EXCEPTION
  WHEN NO_DATA_FOUND THEN
  dbms_output.put_line('不存在工资为'||&v_sal||'的雇员');
WHEN OTHERS THEN
  dbms_output.put_line('错误号:'||sqlcode);
  dbms_output.put_line('错误号:'||sqlerrm);
END;
/
 
第十一章   开发子程序
过程用于执行特定操作而函数用于返回特定数据。
1.建立过程:不带任何参数
DECLARE
IS BEGIN
   dbms_output.put_line(systimestamp);
END;
/
示例一:使用execute命令调用过程
set serveroutput on
exec out_time
实例二:使用call命令调用过程
set serveroutput on
call out_time()
2.建立过程:带有IN参数
CREATE OR REPLACE PROCEDURE  add_employee(eno  NUMBER,name VARCHAR2 ,sal NUMBER,job VARCHAR2 DEFAULT 'CLERK' ,dno number)
IS
 e_intergrity  EXCEPTION;
 PRAGMA  EXCEPTION_INIT(e_integrity ,-2291);
BEGIN
INSERT INTO emp(empno,ename,sal,job,deptno) VALUES(eno,name,sal,jo,dno);
EXCEPTION
 WHEN DUP_VAL_ON_INDEX THEN
   RAISE_APPLICATION_ERROR(-200000,'雇员号不能重复');
WHEN e_intergrity THEN
   RAISE_APPLICATION_ERROR(-200001,'部门号不存在');
END;
/
3.建立过程:带有OUT参数
CREATE OR REPLACE PROCEDURE query_employee
(eno NUMBER,name OUT VARCHAR2,salary OUT NUMBER)
IS
BEGIN
  SELECT ename,sal INTO name,salary FROM emp WHERE empno=eno;
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    RAISE_APPLICATION_ERROR(-200000,'雇员不存在');
END;
/
 
定义之后在数据字典中查看内容:SELECT text FROM user_source WHERE name='ADD+DEPT';
11.2 开发函数
1.建立函数:不带任何参数
CREATE OR REPLACE FUNCTION get_user
RETURN VARCHAR2
IS
  v_user VARCHAR2(100);
begin
  select username INTO v_user FROM user_users;
  RETURN v_user;
END;
/
示例一:使用变量接收函数返回值
var  v1 VARCHAR2(100)
EXEC  :V1:GET_USER
print V1
示例二:在SQL语句中直接调用函数
SELECT get_user FROM dual;
示例三:使用包DBMS_OUTPUT调用函数
SET SERVEROUTPUT ON
exec  dbms_output.put_line(get_user)
2.建立函数:带有IN参数
CREATE OR REPLACE FUNCTION GET_SAL(NAME IN VARCHAR2)
RETURN NUMBER
AS  V_SAL EMP.SAL%TYPE;
BEGIN
 SELECT sal INTO v_sal FROM EMP
   WHERE UPPER(ENAME)=UPPER(NAME);
RETURN V_SAL;
EXCEPTION
  WHEN DO_data_found then
  raise_application_error(-20000,'该雇员不存在');
END;
/
5.函数调用限制
因为函数必须要返回数据,所以只能作为表达式的一部分调用。另外函数也可以在SQL语句的一下部分调用:
SELECT 命令的选择列表
WHERE和HAVING子句中
CONNECT BY ,START WITH,ORDER BY 以及GROUP BY子句中
INSERT命令的VALUES子句中
UPDATE命令的SET子句中
并不是所有函数都可以在SQL语句中调用,在SQL语句中调用函数有以下限制。
在SQL语句中只能调用存储函数,而不能调用客户端的函数
在SQL语句中调用的函数只能带有输入函数而不能带有输出函数和输入输出参数
在SQL语句中调用的函数只能使用SQL所支持的标准数据类型,,而不能使用PL/SQL的特有数据类型 
在SQL语句中调用额函数不能保护INSERT  UPDATE 和DELETE语句。
11.3管理子程序
1.列出当前用户的子程序
数据字典视图USER_OBJECTS用于显示当前用户所包含的所有对象。不仅可以列出用户的表,视图索引等,也可以用于列出用户的过程,函数和包
select object_name,created,status from user_objects where object_type in ('procedure','function')
2.列出子程序源代码
select  text from user_source where name='raise_salary';
3.列出子程序编译错误
1)使用show errors命令确定错误原因和位置
show  errors procedures raise_salary
2)根据数据字典查看
select line ||'/'||position as "line/col",text error from user_errors where name='raise_Salary';
3)重新建立对象raise_salary
4.列出对象依赖关系
1)使用USER_DEPENDENCIES 确定直接依赖关系
select name,type from user_dependencies where referenced_name='EMP';
2)使用工具视图DEPTREE和IDENPTREE确定直接依赖和简洁依赖关系。
@%oracle_home% dbmsadminutldtree
exec  deptree_fill('TABLE','SCOTT','EMP');
 第十二章   开发包
 12.1 建立包
包英语逻辑组合相关的PL/SQL类型、项和子程序,它由包规范和包体两部分组成。当建立包时,需要首先建立包规范,然后再建立包体。
1.建立包规范
包规范实际是包与应用程序之间的接口,他用于定义包的公用组件,包括常量,变量,游标过程和函数等。在包规范中所定义的公用组件不仅可以在包内引用,而且也可以在由其他的子程序引用。
CREATE OR REPLACE PACKAGE package_name 
IS|AS 
   public type and item declarations
   subprogram specificationsEND package_name;
示例:
CREATE OR REPLACE PACKAGE emp_package IS
   g_deptno NUMBER(3):=30;
   PROCEDURE   add_employee(eno  NUMBER,name  VARCHAR2,salary NUMBER,dno  NUMBER  DEFAULT  g_deptno);
   PROCEDURE  fire_employee(eno NUMBER);
   FUNCTION  get_sal(eno NUMBER) RETURN NUMBER;
END emp_package;
/
建立包体
CREATE OR REPLACE PACKAGE BODY package_name 
IS|AS 
  private type and item declarations
  subprogram  bodies
END package_name;
示例:
CREATE OR REPLACE PACKAGE BODY emp_package IS
  FUNCTION validate_deptno(v_depptno NUMBER)
    RETURN BOOLEAN
   IS
     v_temp INT;
   BEGIN
    SELECT 1 INTO v_temp  FROM   dept WHERE deptno=v_deptno;
    RETURN TRUE;
   EXCEPTION
     WHEN NO_DATA_FOUND THEN
         RETURN FALSE;
    END;
    PROCEDURE  add_employee(eno  NUMBER,name VARCHAR2 ,salart NUMBER,dno NUMBER  DEFAULT g_deptno)
    IS
    BEGIN
      IF validate_deptno(dno) THEN 
           INSERT INTO emp(empno,ename,sal,deptno) VALUES (eno,name,salary,dno);
      ELSE 
            raise_application_error(-20000,'不存在该部门');
      END IF;
     EXCEPTION
       WHEN  DUP_VAL_ON_INDEX THEN 
           raise_application_error(-20010,'该雇员已存在');
     END;
     PROCEDURE   fire_employee(eno  NUMBER) IS
     BEGIN
       DELETE FROM emp  WHERE empno=eno;
       IF  SQL%NOTFOUND  THEN
            raise_application_error(-20011,'该雇员不存在');
       END IF;
    END;
    FUNCTION get_sal(eno  NUMBER) RETURN NUMBER
    IS
      v_sal  emp.sal%TYPE;
    BEGIN
      SELECT sal INTO v_sal FROM emp  WHERE empno=eno;
      RETURN v_sal;
    EXCEPTION
      WHEN   NO_DATA_FOUND THEN
         raise_application_error(-20012,'该雇员不存在');
      END;
     END emp_package;
   /
4.查看包的源代码
SELECT text  FROM user_source WHERE name='EMP_PACKAGE' and TYPE='PACKAGE';
第十三章  开发触发器
触发器是指存放在数据库中,并被隐含执行的存储过程。
13.1触发器简介
触发器是指被隐含执行的存储过程。他可以使用PL/SQL,java和C进行开发当发生特定事件(修改表,建立对象登录到数据库时),oracle会自动执行触发器的相应代码。触发器由触发事件,触发条件和触发操作等三部分组成。
1.触发事件
触发事件是指引起触发器被触发的SQL语句、数据库事件或用户事件
具体触发事件有:
启动和关闭数据库
oracle错误信息
用户登录和断开会话
特定表和视图的DML操作
在任何方案上的DDL语句。
2.触发条件
触发条件是指使用WHEN子句指定一个BOOLEAN表达式,当布尔表达式返回值为true时会自动执行触发器相应代码,当布尔表达式返回值为false或unknown时,不会执行触发操作。
3.触发操作
触发操作是指包含SQL语句和其他执行代码的PL/SQL块,不仅可以使用PL/SQL进行开发,也可以使用JAVA语句和C进行开发。但触发条件为TRUE时,会自动执行触发操作的相应代码。但编写触发器执行代码时,有如下限制
1)触发器代码的大小不能超过32k。确实需要使用大量代码建立触发器,应该首先建立存储过程,然后在触发器中使用CALL语句调用存储过程。
2)触发器代码只能包含SELECT INSERT  UPDATE 的DELETE语句,而不能包含DDL语句和事务控制语句
13.2 建立DML触发器
建立DML触发器时,需要是指定触发时机(BEFORE或AFTER),触发事件(insert  update delete),表名,触发类型,触发条件以及触发操作。
1.触发时机
触发时机用于指定触发器的触发时间。当指定BEFORE关键字时,表示在执行DML操作之前触发触发器;当指定AFTER关键字时,表示在执行了DML操作之后触发触发器
2.触发事件
触发事件用于指定导致触发器执行的DML操作,也即INSERT  UPDATE和DELETE操作。即可以使用单个触发事件,也可以组合多个触发事件。
3.表名
因为DML触发器是针对特定表进行的,,所以必须指定DML操作所对应的表
4.触发类型
触发类型用于指定当触发器事件发生之后需要执行几次触发操作。如果在指定语句触发类型,那么只会执行因触发代码。如果指定行触发类型,则会在每个被作用行上执行一次触发器代码。
5.触发操作
指定触发器执行代码。如果使用PL/SQL存储过程,java存储过程或外部存储过程来实现触发器代码,那么在触发器操作部分可直接使用CALL语句调用相应过程。如果使用PL/SQL匿名块编写触发操作
[DECLARE]
   --定义变量  常量等
BEGIN
  --编写SQL语句和PL/SQL语句
EXCEPTION 
  --编写例外处理语句
END;
7.DML触发器触发顺序
1)DML触发器在单行数据上的触发顺序
当针对某一表的相同DML操作而建立了多个DML触发器(BEFORE、AFTER语句触发器 BEFORE AFTER行触发器)时,如果在单行数据上执行了该种DML操作,则触发器会按照以下顺序执行:
--BEFORE语句触发器
--BEFORE行触发器
--AFTER行触发器
--BEFORE行触发器
--AFTER行触发器
--BEFORE行触发器
--AFTER行触发器
--AFTER语句触发器
 
13.2。1语句触发器
语句触发器是指当执行DML语句时被隐含执行的触发器。如果在表上针对某种DML操作建立了语句触发器,那么当执行DML操作时会自动执行触发器的相应代码当审计DML操作,或者确保DML操作安全执行时,可以使用语句触发器
CREATE OR REPLACE TRIGGER trigger_name
timing event1[OR event2 ORevent3]
ON table_name
PL/SQL block;
--trigger_name用于指定触发器名:timing用于指定触发时机(before  after)
event用于指定触发事件(insert   update  delete);table_name用于指定DML操作所对应的表名。
1.建立BEFORE语句触发器
为了确保DML操作在正常情况下执行,可以基于DML操作建立BEFORE语句触发器
CREATE OR REPLACE TRIGGER tr_sec_emp
before insert or update on emp
BEGIN
  IF to_char(sysdate,'DY','nls_date_language=AMERICAN')
   IN ('SAT','SUN') THEN
   raise_application_error(-200001,'不能在休息日改变雇员信息');
  END IF;
END;
/
2.使用条件谓词
当在触发器中同时包含多个触发器事件(INSERT  UPDATE  DELETE)时,为了在触发器代码中区分具体的触发事件可以使用如下三个条件谓词:
 --INSERTING:当触发事件是INSERT操作时,该条件谓词返回值为TRUE否则为FALSE
 --UPDATING:当触发事件是UPDATE操作时,该条件谓词返回值为TRUE,否则为false
 --DELETING:当触发事件是DELETE操作时,该条件谓词返回值为TRUE否则为FALSE.
示例:
 CREATE OR REPLACE TRIGGER tr_sec_emp
 BEFORE INSERT OR UPDATE OR DEPETE ON emp
 BEGIN
    IF to_char(sysdate,'DY','nls_date_language=AMERICAN')
     IN ('SAT','SUN') THEN
    CASE
      WHEN INSERTING THEN
        raise_application_error(-200001,'不能在休息日增加雇员');
      WHEN   UPDATING THEN
         raiae_application_error(-20003,'不能在虚席日解雇雇员');
    END CASE;
   END IF;
 END;
/
3.建立AFTER语句触发器
示例
CREATE OR REPLACE TRIGGER  tr_audit_emp
AFTER  INSERT OR UPDATE OR DELETE ON emp
DECLARE
  v_temp INT;
BEGIN
  SELECT COUNT(*) INTO V_TEMP FROM audit_table WHERE name='emp';
  IF v_temp=0 THEN
     INSERT INTO audit_table VALUES ('EMP',0,0,0,SYSDATE,NULL);
  END IF;
  CASE
    WHEN INSERTING THEN
     UPDATE audit_table SET INS=ins+1,endtime=SYSDATE WHERE NAME='EMP';
    WHEN   UPDATING  THEN
     UPDATE audit_table SET UPD=UPD+1,endtime=SYSDATE WHERE NAME='EMP';
    WHEN   DELETING THEN
     UPDATE audit_table SET del=del+1,endtime=SYSDATE WHERE NAME='EMP';
  END CASE;
END;
/
13.2.2 行触发器
行触发器是指执行DML操作时,每作用一行就触发一次的触发器
CREATE OR REPLACE TRIGGER TRIGGER_NAME
timing  event1 ON table+name
[REFERENCING OLD AS old|NEW AS NEW]
FOR EACH  ROW
[WHEN  condition]
PL/SQL block;
--
trigger_name用于指定触发器名,timing用于指定触发时机(BEFORE 或AFTER);event用于指定触发事件(insert  update  delete);REFERENCEING子句用于指定引用新 旧数据的方式,默认情况下使用old修饰符引用就数据,使用new修饰符引用新数据,table_name用于指定DML操作所对应的表。FOR EACH ROW表示建立行触发器
WHEN子句用于指定触发条件
1.建立BEFORE行触发器
CREATE OR REPLAE TRIGGER tr_emp_sal
BEFORE  UPDATE OF sal ON emp 
FOR EACH ROW
BEGIN
 IF  :new.sal<:old.sal THEN 
     raise_application_error(-200010,'工资只涨不降')
  END IF;
END;
/
2.建立AFTER触发器
CREATE TABLE audit_emp_change(name VARCHAR2(20),OLDSAL NUMBER(6,2),NEWSAL NUMBER(6,2),TIME DATE)  ;
示例:
CREATE OR REPLACE TRIGGER tr_sal_change
AFTER UPDATE OF sal ON emp
FOR  EACH ROW
DECLARE
   v_temp  INT;
BEGIN
   SELECT count(*) INTO v_temp FROM audit_emp_change
   WHERE name:=old.ename;
IF  v_temp=0 THEN
   INSERT INTO audit_emp_change
      VALUES(:old.ename,:old.sal,:new.sal,SYSDATE);
ELSE
  UPDATE audit_emp_change
     SET oldsal:=old.sal,newsal:=new.sal,time=SYSDATE
      WHERE name=:old.ename;
END IF;
END;
3.限制行触发器
CREATE OR REPLACE TRIGGER tr_sal_change
AFTER UPDATE OF sal ON emp
FOR EACH ROW 
WHEN (old.job='SALESMAN')
DECLARE
   v_temp  INT;
BEGIN
  SELECT count(*) INTO v_temp FROM audit_emp_change
   WHERE  name=:old.ename;
IF   v_temp=0  THEN
    INSERT INTO audit_emp_change
     VALUES(:old.ename,:old.sal,:new.sal,SYSDATE);
ELSE
    UPDATE audit_emp_change
     SET oldsal=:old.sal,newsal=:new.sal,time=SYSDATE 
      WHERE name=:old.ename;
END IF;
END;
/
4.DML触发器使用注意事项
当编写DML触发器时,触发器代码不能从触发器所对应的基表中读取数据。
13.2.3 使用DML触发器
1.控制数据安全
在服务器级控制数据安全是通过授予和收回对象权限来完成的
GRANT  SELECT ,INSERT ,UPDATE ,DELETE ON emp TO SMITH;
 
CREATE OR REPLACE  TRIGGER tr_emp_time
BEFORE INSERT OR UPDATE OR DELETE ON emp
BEGIN
IF  to_char(SYSDATE,'HH24') NOT BETWEEN '9' AND '17' THEN
   raise_application_error(-20101,'非工作时间');
END IF;
END;
/
13.3  建立INSTEAD OF 触发器
对于简单视图,可以直接执行INSERT  UPDATE和DELETE操作。但是对于复杂视图,不允许直接执行INSERT  UPDATE DELETE .当视图符合以下一种情况时,都不能直接执行DML操作。具体情况如下:
具有集合操作符(UNION,UNION ALL,INTERSECT,MINUS);
具有分组函数(MIN,MAX SUM,AVG  ,COUNT等);
具有GROUP BY,CONNECT BY或START WITH等子句。
具有DISTINCT关键字
具有连接查询。
建立INSTEAD-OF触发器时的注意事项:
INSTEAD OF选项只适用于视图
当基于视图建立触发器时,不能指定BEFORE和AFTER选项
在建立视图时没有指定WITH CHECK OPTION选项
当建立INSTEAD OF触发器时,必须指定INSTEAD=OF触发器的方法。
13.4 建立系统事件触发器
1.常用事件属性函数
ora_client_ip_address:用于返回客户端的IP地址
ora_database_name:用于返回当前数据库名
ora_des_encrypted_passwd:用于返回DES加密后的用户口令
ora_dict_obj_name:用于返回DDL操作所对应的数据库对象名
ora_dict_obj_name_list:用于返回在事件中被修改的对象名列表
ora_dict_obj_owner:用于返回DDL操作所对应的对象的所有者名
ora_dict_obj_owner_list:用于返回在事件中被修改对象的所有者列表
ora_dict_obj_type:用于返回DDL操作所对应的数据库对象的类型
ora_grantee:返回授权事件的授权者
ora_instance_num:返回例程号
ora_is_alter_column:用于检测是否正在建立嵌套表
ora_is_drop_column:用于检测特定列是否被删除
ora_is_servererror:用于检测是否返回了特定的oracle错误
ora_login_user:用于返回登录用户名
ora_sysevent:返回触发器的系统事件名。
 
第十四章 :开发动态SQL
14.1 动态SQL简介
1.静态SQL
直接嵌入在PL/SQL块中的SQL语句,在编写PL/SQL块时静态SQL用于完成特定的活固定的任务
2.动态SQL
在运行PL/SQL块时动态输入的SQL语句。如果在PL/SQL块中需要执行DDL语句,DCL语句,或者PLSQL块中需要执行更加灵活的SQL语句,那么就必须使用动态SQL。在PL/SQL块中编写动态SQL语句时,需要将SQL语句存放到字符串变量中,而且SQL语句也可以包含占位符)—以冒号开始)。
3.静态SQL和动态SQL的比较
静态SQL是在编写PL/SQL块时直接嵌入的SQL语句;而动态SQL是在运行PLSQL快时动态输入的SQL语句。
静态SQL性能要优于动态SQL,因此当编写PLSQL块时,如果功能完全确定,则使用静态SQL,如果不能确定要执行的SQL语句,则使用动态SQL。
4.动态SQL的处理方法
1)使用EXECUTE  IMMEDIATE语句
EXECUTE IMMEDIATE语句可以处理多数动态SQL操作,包括DDL  DCL  DML以及单行的SELECT 语句,但是这种方法不能处理多行查询语句。
2)使用OPEN-FOR,FETCH和CLOSE语句。
为了处理动态的多行查询操作,必须要使用OPEN-FOR语句打开游标使用FETCH语句循环提取数据,最终使用CLOSE语句关闭游标
3)使用批量动态SQL
通过使用批量动态SQL,而且加快SQL语句处理,进而提高PL/SQL程序的性能。
14.2 处理非查询语句
EXECUTE IMMEDIATE DYNAMIC_STRING
 
 
 
 
 
 
 
 
 
 
 
原文地址:https://www.cnblogs.com/dream-to-pku/p/5317265.html