Oracle Database-PL/SQL
Posted zella1996 (部分文章代码格式错误见谅)
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Oracle Database-PL/SQL相关的知识,希望对你有一定的参考价值。
Oracle Database-PL/SQL
PL/SQL基础
PL/SQL(Procedure Language/SQL)
PL/SQL是Oracle对SQL语言的过程化扩展,指在SQL命令语言中增加了过程处理语句(如分支、循环等),使SQL语言具有过程处理能力。把SQL语言的数据操纵能力与过程语言的数据处理能力结合起来,使得 PLSQL面向过程但比过程语言简单、高效、灵活和实用。
因为是过程化扩展,所以
因为是过程化扩展,所以
- PL/SQL程序内支持编写SQL语言
- 将SQL语言的数据操纵能力与过程语言的数据处理能力结合
- PL/SQL是一门面向过程的语言
PL/SQL的官方文档
如下,可以查到DBMS_OUTPUT程序包的内容
也可以在命令行中使用desc关键字直接查看程序包的结构
如下,这种方法相当于简化的文档
PL/SQL的语法
*Java的语法
PL/SQL可以完成以下Java代码(伪)完成的功能
下面是PL/SQL实现这个功能
而PL/SQL相对JDBC实现这个更能更有效率
开发模式:SpringMVC+存储过程(PL/SQL)
Hello World
这个dbms_output是一个程序包(类似API)
declare-变量和常量的说明
注意:若没有需要说明的变量或常量,可以没有declare关键字
说明基本数据类型变量时要说明三个部分
- 变量名
- 数据类型
- 数据大小
基本类型变量
与表的列的数据类型对应(意味着我们可以把表中的数据存到这些类型的变量中)
- char
- varchar2
- date
- number
- boolean
- long
*PL/SQL中的":="相当于Java中的"=","="相当于Java中的"=="
引用型变量
引用表中列的数据类型作为该变量的数据类型
示例:
赋值有两种方法,":="和"into"关键字
记录型变量
记录型变量引用表中的一行作为变量(s)的数据类型,换句话说,记录型变量可以存放表中的一行数据,可以理解成一个数组(或者集合),数组中的每一个元素代表这一行的记录每一列
示例:
IF语句
语法
注意elsif这个写法与Java else if不一样
示例:
-- 判断用户从键盘输入的数字并打印相应语句 -- 接收键盘输入 -- accept addr prompt \'xxx\' -- addr 是一个地址值,在该地址上保持了输入的值 accept num prompt \'请输入一个数字\'; declare pnum number := # -- 注意:取出存在在这个地址值的值要使用\'&\'符号相当于c的指针 begin if pnum = 0 then dbms_output.put_line(\'you input 0\'); else if pnum = 1 then dbms_output.put_line(\'you input 1\'); else then dbms_output.put_line(\'you input other number\'); end if; end;
注意,这段正确的代码无法在PL/SQL Developer和命令行中运行,但可以在Oracle SQL Developer(自行下载)中运行
循环语句
语法
示例:输入数字1到10
Cursor-光标(游标)
在Java语言中有集合的概念,在PL/SQL语言中也会用到多条记录作为整体,此时我们需要用到游标,游标可以存储查询返回的多条数据
简言之,光标/游标类似Java中的ResultSet
基本语法
说明光标
CURSOR 光标名 [(参数名数据类型[,参数名数据类型]...)] IS SELECT 语句;
如:
使用步骤
fetch关键字的作用是将光标的一个值赋到变量中并将指针往后移动一位(游标的指针默认在第一个值上)
示例:
光标的属性
- %isopen:光标是否已打开
- %rowcount:影响的行数(即已取出的行数)
- %found:当前指针的位置是否仍然有记录
- %notfound:当前指针位置是否没有记录
带参数的光标
语法
示例:
例外
例外例外是程序设计语言提供的一种功能,用来增强程序的健壮性和容错性
相当于Java中的异常
例外的分类
- 系统定义的例外
- No_data_found(没有找到数据)
- Too_many_rows(select …into语句匹配多个行)
- Zero_Divide(被零除)
- Value_error(算术或转换错误)
- Timeout_on_resource(在等待资源时发生超时)(分布式数据库相关)
- 用户定义的例外
定义及处理例外
- 在declare节中定义例外
- out_of exception;
- 在可行语句中引起例外
- raise out_of;(类似Java中的throw)
- 在exception节处理例外
- when out_of then …;(类似Java中的try-catch)
示例:
*PL/SQL的应用
实例
declare --部门 cursor cdept is select deptno from dept; pdeptno dept.deptno%type; --部门中员工的薪水 cursor cemp(dno number) is select sal from emp where deptno=dno; psal emp.sal%type; --每个段的人数 count1 number; count2 number; count3 number; --部门的工资总额 salTotal number := 0; begin --部门 open cdept; loop --取一个部门 fetch cdept into pdeptno; exit when cdept%notfound; --初始化 count1:=0; count2:=0; count3:=0; --得到部门的工资总额 select sum(sal) into salTotal from emp where deptno=pdeptno; --取部门的中员工薪水 open cemp(pdeptno); loop --取一个员工的薪水 fetch cemp into psal; exit when cemp%notfound; --判断 if psal < 3000 then count1:=count1+1; elsif psal >=3000 and psal<6000 then count2:=count2+1; else count3:=count3+1; end if; end loop; close cemp; --保存结果 insert into msg values(pdeptno,count1,count2,count3,nvl(saltotal,0)); end loop; close cdept; commit; dbms_output.put_line(\'完成\'); end;
编写PL/SQL程序的一般步骤
- 需求分析
- SQL语句:即涉及操作数据库的逻辑
- 获取数据库中的数据
- 使用什么类型的变量来存放获取到的数据
- 使用什么方式来从变量中提取数据(如循环,判断等)
- 修改数据库中的数据
- 变量
- 变量的初始值
- 变量的获取方式
- 满足什么条件就改变变量的值
PL/SQL进阶内容
存储过程和存储函数
存储过程
存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的SQL语句集经编译后存储在数据库中,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。
存储过程是数据库中的一个重要对象,任何一个设计良好的数据库应用程序都应该用到存储过程。
存储函数
函数(Function)为一命名的存储程序,可带参数,并返回一计算值。函数和过程的结构类似,但必须有一个RETURN子句,用于返回函数值。函数说明要指定函数名、结果值的类型,以及参数类型等。
*
简言之,存储在数据库中供所有用户程序调用的由PL/SQL编写的子程序被称为存储过程或存储函数。
在Java程序中,不能直接调用PL/SQL编写好的程序,但是可以调用存储过程或存储函数。
当然,在存储过程或存储函数中也可以调用存储过程或存储函数。
存储过程和存储函数的区别
一般来讲,过程和函数的区别在于函数可以有一个返回值:而过程没有返回值。但过程和函数都可以通过out指定一个或多个输出参数。我们可以利用out参数,在过程和函数中实现返回多个值。
通常除去return(返回)值的不同,可以认为两者相同。
使用存储过程的语法
创建存储过程
调用存储过程
示例:
示例:
使用存储函数的语法
创建存储函数
调用存储函数
示例
存储过程和存储函数中的in(输入参数)和out(输出参数)
一般来讲,过程和函数的区别在于函数可以有一个返回值,而过程没有返回值。但过程和函数都可以通过out指定一个或多个输出参数,我们可以利用out参数,在过程和函数中实现返回多个值。
使用out参数来返回值
在out参数中返回游标
使用存储过程/函数的注意事项
如果只有一个返回值,用存储函数,否则,就用存储过程
尽量不在存储过程或存储函数中操作事务,因为子程序是交由其他人调用的,不应该操作事务
*Java调用存储过程/存储函数
创建测试项目,导包
包的位置位于安装Oracle的机器下的这个目录
导入项目如下
编写工具类
package demo; import java.sql.*; public class JDBCUtils { private static String driver = "oracle.jdbc.OracleDriver"; private static String url = "jdbc:oracle:thin:@169.254.35.157:1521/orcl"; private static String user = "scott"; private static String password = "tiger"; static{ // 注册驱动 try { Class.forName(driver); } catch (ClassNotFoundException e) { throw new ExceptionInInitializerError(e); } } public static Connection getConnection() { try { return DriverManager.getConnection(url, user, password); } catch (SQLException e) { e.printStackTrace(); } return null; } public static void release(Connection conn, Statement st, ResultSet rs) { if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } finally{ rs = null; // 置空作为垃圾回收 } } if (st != null) { try { st.close(); } catch (SQLException e) { e.printStackTrace(); } finally{ st = null; } } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } finally{ conn = null; } } } }
Statement接口的子接口CallableStatement就是专门用于执行SQL存储过程的接口
其中红框内是调用存储过程的标准方式
编写测试类分别测试存储过程和存储函数
存储过程
存储过程如下
测试代码如下
/* procedure queryEmpInformation(eno in number, pename out varchar2, psal out number, pjob out varchar2) */ @Test public void testProcedure() { String sql = "{call queryEmpInformation(?,?,?,?)}"; Connection conn = null; CallableStatement call = null; try { conn = JDBCUtils.getConnection(); call = conn.prepareCall(sql); // 对于in参数,需要对其赋值 call.setInt(1, 7839); // 对于out参数,需要对其声明 call.registerOutParameter(2, OracleTypes.VARCHAR); call.registerOutParameter(3, OracleTypes.NUMBER); call.registerOutParameter(4, OracleTypes.VARCHAR); // 执行调用存储过程 call.execute(); // 从CallableStatement对象中取出输出out参数 String name = call.getString(2); Double sal = call.getDouble(3); String job = call.getString(4); System.out.println("name:" + name + " sal:" + sal + " job:" + job); } catch (SQLException e) { e.printStackTrace(); } finally { JDBCUtils.release(conn, call, null); } }
输出如下
存储函数
存储过程如下
测试代码如下
/* function queryEmpIncome(eno in number) return number */ @Test public void testFunction() { String sql = "{?= call queryEmpIncome(?)}"; Connection conn = null; CallableStatement call = null; try { conn = JDBCUtils.getConnection(); call = conn.prepareCall(sql); // 存储函数的输出参数即返回值,是第一个问号,对其注册 call.registerOutParameter(1, OracleTypes.NUMBER); // 赋值输入参数 call.setInt(2, 7839); call.execute(); double annIncome = call.getDouble(1); System.out.println("annual income:" + annIncome); } catch (SQLException e) { e.printStackTrace(); } finally { JDBCUtils.release(conn, call, null); } }
输出如下
调用使用光标作为out参数的存储过程
问题:查询某个部门中所有员工的所有信息
在编写PL/SQL时申明包结构
创建包
在包中可以自定义一个数据类型(下面的empcursor),引用原有的数据类型(下面引用了cursor光标类型)作为这个类型
创建包体
在包体中需要实现包头中定义的所有存储过程和存储函数
在Java中访问
使用ResultSet来接受光标参数
具体测试代码如下
@Test public void testCursor() { String sql = "{call mypackage.queryemplist(?,?)}"; Connection conn = null; CallableStatement call = null; try { conn = JDBCUtils.getConnection(); call = conn.prepareCall(sql); call.setInt(1, 10); call.registerOutParameter(2, OracleTypes.CURSOR); call.execute(); // CallableStatement是一个通用的接口,获取时获得Oracle适用的实现类对象,可以将其强制转换成Oracle适用的接口 ResultSet cursor = ((OracleCallableStatement) call).getCursor(2); while (cursor.next()) { System.out.println("name:"+cursor.getString("ename")); System.out.println("hiredate:"+cursor.getDate("hiredate")); } } catch (SQLException e) { e.printStackTrace(); } finally { JDBCUtils.release(conn, call, cursor); // 此处关闭ResultSet时同时关闭了这个光标 } }
触发器
数据库触发器是一个与表相关联的、存储的PL/SQL程序。每当一个特定的数据操作语句(Insert,update和delete不包括select)在指定的表上发出时,Oracle自动地执行触发器中定义的语句序列。
触发器的作用
- 数据确认
- 如:员工涨工资后的工资不能少于涨工资后的工资
- 实施复杂的安全性检查
- 如:禁止在非工作时间插入新员工
- 审计,跟踪表上的数据操作(日志)
- *Oracle中的审计有五种
- 强制审计
- 标准审计(配置)
- 基于值的审计(触发器审计属于这一类
- 细粒度审计
- 管理员审计
- 数据的备份和同步
触发器的类型
-
语句级触发器
-
在指定的操作语句操作之前或之后执行一次,不管这条语句影响了多少行
- 如:一次插入多条数据,触发器只执行一次
-
针对的是表
-
行级触发器
-
触发语句作用的每一条记录都被触发
-
在行级触发器中使用:old和:new伪记录变量,识别值的状态
-
针对的是行
创建触发器
语法
示例:
限制非工作时间向数据库插入数据
当非工作时间插入员工执行时会报错
示例2:
确认数据(检查emp表中sal 的修改值不低于原值)
运行效果
查询触发器、过程及函数
- select * from user_triggers;
- select * from user_source;
行级触发器中触发语句与伪记录变量代表的值
触发器的实际应用
S
*使用PL/SQL Developer编写PL/SQL程序
Hello World
以上是关于Oracle Database-PL/SQL的主要内容,如果未能解决你的问题,请参考以下文章
ORA-00600: internal error code, arguments: [25027], [1], [0], [], [], [], [], [], [], [], [], [](代码片
ORA-00600: internal error code, arguments: [25027], [1], [0], [], [], [], [], [], [], [], [], [](代码片