一、概述
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%\rdbms\admin\utldtree
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
- 安装Oracle 10g
本文仅用于学习交流,商业用途请支持正版!转载请注明: http://www.cnblogs.com/mxbs/p/6217052.html 准备: Oracle 10g for Win(32-Bit) ...
- Oracle 10g安全加固(审计、监听密码)
环境: Linux 6.4 + Oracle 10.2.0.4 1. Oracle 10g 审计功能 2. 对数据库监听器的关闭和启动设置密码 1. Oracle 10g 审计功能 Oracle 10 ...
- ORACLE 10g 数据库体系结构图
ORACLE 10g 的数据库体系结构图(ORACLE 10g(Release 2)ARCHITECTURE),非常的全面.系统.高屋建瓴的整体介绍了ORACLE 10g 的数据库体系结构.如果能全面 ...
- RHEL6 64位系统安装ORACLE 10g 64bit 数据库
记得去年4月份的时候,为公司部署测试环境和UAT环境时,在红帽RHEL6 64位系统安装ORACLE 10g 64位数据库时遇到了许多小问题,当时匆匆忙忙也没记录一下这些问题,前几天在虚拟机安装ORA ...
- windows 7/10下安装oracle 10g
有段时间没搞oracle了,最近要给别人在win 7下装个oracle 10g,特记录备忘下. 使用http://download.oracle.com/otn/nt/oracle10g/10201/ ...
- ORACLE 10g AWR报告设置总结
1:查看.修改AWR报告快照数据的采样间隔.保存策略 SQL> COL DBID FOR 999999999999 SQL> COL SNAP_INTERVAL FOR A26 SQL ...
- CENTOS 6.4 安装oracle 10g,手工建库及升级到10.2.0.5
一. 数据库软件安装 参照官方手册 1.安装rpm包 注这里的yum直接用163的yum yum -y install binutils compat-libstdc++-33 compat-libs ...
- [转]oracle 10g数据泵之impdp-同时导入多个文件
要了解impdp,请先了解导出,我之前作过导出的笔记:oracle 10g数据泵之expdp.这两个笔记也许只对程序员有用,通常用于把正式区的数据导入到测试区,对数据库管理员也许帮助不大,他们使用这些 ...
- ORACLE 10g下载地址
ORACLE 10g下载地址 oracle 下载还需要用户名我自己注册了个方便大家使用下载 密码是这个 一般不会动了 大家也不用帮我找回密码了 每次都改 也很麻烦的如果有需要可以帮我浏览下 右侧的需要 ...
随机推荐
- android AsyncTask实例
.java package com.example.activitydemoay; import android.app.Activity; import android.content.Intent ...
- Error while performing database login with the sqljdbc driver:Unable to create connection. Check your URL.
从微软官网下载jdbc驱动包sqljdbc4,运行sqljdbc_4.0.2206.100_chs.exe,将驱动包解压到了Microsoft JDBC Driver 4.0 for SQL Serv ...
- 【转】Android编程判断手机or平板
转载自:http://www.cnblogs.com/sunzn/p/3663363.html /** * 判断当前设备是手机还是平板,代码来自 Google I/O App for Android ...
- 修改TFS客户端的工作区类型
TFS系统存在两种工作区类型:"本地"和"服务器".默认情况下,用户使用本地工作区实现代码管理. 但是在用户端代码文件特别多(超过10万个文件)时,由于每次启 ...
- 磁盘、分区及Linux文件系统 [Disk, Partition, Linux File System]
1.磁盘基础知识 1.1 物理结构 硬盘的物理结构一般由磁头与碟片.电动机.主控芯片与排线等部件组成:当主电动机带动碟片旋转时,副电动机带动一组(磁头)到相对应的碟片上并确定读取正面还是反面的碟面,磁 ...
- 网页缩放对 FLASH的影响
目前新出的,和升级的浏览器,都加了页面放大功能, 这些功能是对FLASH有影响的,表现在 flash在获取stage.stageWidth时,数值会按相应比例有变化 本人在用flex4.6开发时,自定 ...
- easyUI的dateBox控制时间格式
<input type='text' name='yearQuery' class='easyui-datebox ' data-options="formatter:myformat ...
- Oracle 中 decode 函数用法(转)
含义解释: decode(条件,值1,返回值1,值2,返回值2,...值n,返回值n,缺省值) 该函数的含义如下: IF 条件=值1 THEN RETURN(翻译值1) ELSIF 条件=值2 THE ...
- HTML-学习笔记(样式)
HTML 样式 style属性用于改变HTML元素的样式. <p style="font-family: arial; color: red;">字体是arial,字体 ...
- http协议(三)几种数据传输方式
说说http协议的一些特点: 1)无状态 http协议是一种自身不对请求和响应之间的通信状态进行保存的协议,即无状态协议. 这种设置的好处是:更快的处理更多的请求事务,确保协议的可伸缩性 不过随着we ...