Oracle实战笔记(第六天)之PL/SQL基础
Posted 风之之
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Oracle实战笔记(第六天)之PL/SQL基础相关的知识,希望对你有一定的参考价值。
一、PL/SQL介绍
1、概念
PL/SQL也是一种程序语言,叫做过程化SQL语言(Procedural Language/SQL)。PL/SQL是Oracle数据库对SQL语句的扩展。在普通SQL语句的使用上增加了编程语言的特点,所以PL/SQL可以用来编写存储过程、存储函数、触发器等等。 PL/SQL是结构化SQL,就是在标准SQL中加入了IF...ELSE... For....等控制过程的SQL。
2、学习必要性
- 提高程序的运行性能
传统的使用程序操作数据库的方式需要获得connection,然后程序传到数据库后还需要进行编译才能执行,而且执行结果还需要回调,这些过程都需要花费时间。而PL/SQL编程可以直接进行过程编程,这就节省了数据连接、语句传送和编译的时间,同时编程结果可以被外部程序调用。 - 模块化的设计思想
很多复杂的过程都需要传送和执行大量的sql语句,比如分页的过程、实现订单的过程、转账的过程等等,这些过程就可以通过PL/SQL进行模块化整理进而方便管理。 - 减少网络传输量
- 提高安全性
PL/SQL的缺点在于移植性不好,如果项目不再使用Oracle来管理数据,那么需要重新编写语句。
二、开发工具
1、Oracle自带的命令行工具sql*plus
下面举一个运用PL/SQL语句的案列:编写一个存储过程,并执行。
简单说一下,存储过程(Stored Procedure)的概念:在大型数据库系统中,一组为了完成特定功能的SQL语句集,存储在数据库中,经过一次编译后不需要再进行二次编译,用户直接通过指定该过程的名字,如果有参数并给出参数来执行它。类似编程里的函数的功能。
-
--1、创建一个表 create table test(name varchar2(10),password varchar2(20)); --2、创建一个存储过程 create procedure proc1 is begin--开始 insert into test values(\'tom\',\'tom123\');--执行过程 end;--结束 /--创建标志 (注意:存储过程的命名需要唯一,如果重复需要更换一个名字或使用replace命令来替代已存在的存储过程) --3、调用过程 exec 过程名(参数1,参数2...);(execute执行的缩写) call 过程名(参数1,参数2...);
这只是存储过程使用的一个最基本案列,先简单体验一下。
2、非官方工具:PL/SQL Developer
我们首先要安装PL/SQL Developer工具,安装成功并配置相关文件后就可以使用了。使用同上面一样的案例(这里只是简单演示,后面会重点使用)
三、块(Block)
块是PL/SQL的基本程序单元,编写PL/SQL其实就是编写PL/SQL块。一个块可以实现一个简单的功能,而复杂的功能可能需要在一个块中嵌套其他若干块。(感觉很像一个函数段)
1、块的组成
pl/sql 块由一般由三个部分构成:定义部分,执行部分,例外处理部分。
-
declare /*[可选]定义部分——定义常量、变量、游标、例外、复杂数据类型*/ begin /*[必须]执行部分——要执行的pl/sql语句和sql语句*/ exception /*[可选]例外处理部分——处理运行的各种错误*/ end;
注:定义部分是从declare开始的,该部分是可选的; 执行部分是从begin开始的,该部分是必须的; 例外处理部分是从exception开始的,该部分是可选的。
2、PL/SQL的编写内容
PL/SQL可以编写的主要内容有(注:Oracle中的数据对象包括:索引、表、视图、函数、包、过程、同义词、序列、触发器):
- 过程(存储过程)
- 函数
- 包
- 触发器
3、编写规范
- 注释
--这是单行注释 /* 这是多行注释 */
- 命名规范
--变量命名规范:以v_作为前缀 v_variable --常量命名规范:以c_作为前缀 c_constant --游标命名规范:以_cursor作为后缀 emp_cursor --例外命名规范:以e_作为前缀 e_error
4、pl/sql块示例
-
--示例1:只包括执行部分 set serveroutput on --打开输出选项 begin dbms_output.put_line(\'hello\'); end; --说明: dbms_output是oracle提供的包,类似Java的包,包中有若干过程,如put_line就是该包的过程,可以输出指定参数。
-
--示例2:包含定义部分和执行部分的pl/sql块 declare v_ename varchar2(5); --定义字符串变量 begin select ename into v_ename from emp where empno=&empno; dbms_output.put_line(\'雇员名:\'||v_ename); end; --说明:into表示将查询的结果赋给变量v_ename;&表示需要从控制台输入变量;||表示字符串拼接,也可以使用concat函数来拼接。
- 为了避免pl/sql程序的运行错误,提高pl/sql的健壮性,应该对可能的错误进行处理,这个很有必要。比如在实例2中,如果输入了不存在的雇员号,应当做例外处理。有时出现异常,希望用另外的逻辑处理。oracle事先预定义了一些例外,no_data_found就是找不到数据的例外。
--示例3:包含declare、begin、exception三部分 declare --定义变量 v_ename varchar2(5); v_sal number(7,2); begin --执行部分 select ename,sal into v_ename,v_sal from emp where empno=&empno; --在控制台显示用户名 dbms_output.put_line(\'用户名是:\'||v_ename||\' 工资:\'||v_sal); --异常处理 exception when no_data_found then dbms_output.put_line(\'你的编号输入不符合规则!\'); end; /
四、过程(Procedure)
1、概念
存储过程(Stored Procedure)的概念:在大型数据库系统中,一组为了完成特定功能的SQL语句集,存储在数据库中,经过一次编译后不需要再进行二次编译,用户直接通过指定该过程的名字,如果有参数并给出参数来执行它。类似编程里的函数的功能,但是有区别,因为Oracle本身也具有函数。过程可以指定输入参数(in)将执行环境需要的数据传入,也可以指定输出数据(out)将执行环境中的数据导出到应用环境中。类似函数的参数和返回值。
2、示例
-
--创建一个过程:为指定员工增加指定工资 create or replace procedure pro_AddSal(v_ename varchar2,v_addsal number) is begin update emp set sal=sal+v_addsal where ename=v_ename; end; / --调用过程:为SCOTT增加1000元工资 exec pro_AddSal(\'SCOTT\',1000);
我们可以也使用外部程序例如Java程序来执行:
1 public static void main(String[] args){ 2 try{ 3 //1、使用jdbc加载驱动 4 Class.forName("oracle.jdbc.driver.JdbcDriver"); 5 //2、获得连接 6 Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@192.168.183.1:1521:orcl","Scott","tiger"); 7 //3、创建存储过程的调用对象:CallableStatement 8 CallableStatement cs = conn.prepareCall("{call pro_AddSal(?,?)}"); 9 //4、传入参数 10 cs.setString(1,"SMITH"); 11 cs.setInt(2,500); 12 //5、执行 13 cs.excute(); 14 //6、关闭资源 15 cs.close(); 16 conn.close(); 17 }catch(Exception e){ 18 e.printStackTrace(); 19 } 20 }
五、函数(Function)
1、概念
函数用于返回特定的数据。当建立函数时,在函数头部必须包含return子句,而在函数体内必须包含return语句返回的数据。使用create function来建立函数。
2、示例
-
--函数示例:输入雇员的姓名,返回该雇员的年薪 --函数头:定义 create function fun_showYearSal(v_ename varchar2) return number is yearSal number(7,2); begin --函数体:执行 select sal*12+nvl(comm,0)*12 into yearSal from emp where ename=v_ename; return yearSal; end;
-
--调用函数 var yearSal number;--定义一个变量用来接收返回值 call fun_showYearSal(\'SCOTT\') into:yearSal;--调用函数 print yearSal;--显示返回值
六、包(Package)
1、概念
用于在逻辑上组合过程和函数。由包规范和包体两部分组成。使用create package命令来创建包。
2、示例
-
--创建包Create Package --包规范:起到声明作用 create package myPackage is procedure update_sal(name varchar2,newsal number);--过程 function annual_income(name varchar2) return number;--函数 end; /
-
--实现包体:create package body create or replace package body myPackage is --实现过程 procedure update_sal(name varchar2,newsal number) is begin update emp set sal=newSal where ename=name; end; --实现函数 function annual_income(name varchar2) return number is annual_salary number(7,2); begin select sal*12+nvl(comm,0) into annual_salary from emp where ename=name; return annual_salary; end; end; /
-
--调用包下的一个过程:call或exec call myPackage.update_sal(\'SCOTT\',1500);
七、触发器(Trigger)
触发器是在事件发生时隐式地自动运行的PL/SQL程序块,不能接收参数,不能被调用。当定义触发器时,必须指定触发的事件和触发的操作。
常用的触发事件包括:insert,update,delete语句。触发操作实际就是一个pl/sql块。可以使用create trigger来建立触发器。说明:触发器可以维护数据库的安全和一致性。
触发器是很有用的数据对象,这里先简单了解。
八、变量
1、类型
- 标量类型(scalar)
- 复合类型(composite)
- 参照类型(reference)
- lob(large object)
2、标量类型(scalar)
标量的定义:亦称“无向量”。有些物理量,只具有数值大小,而没有方向,部分有正负之分。物理学中,标量(或作纯量)指在坐标变换下保持不变的物理量。用通俗的说法,标量是只有大小,没有方向的量。
1)语法
- identifier:名称
- constant:常量。需要制定初始值,而且不能够改变。
- datatype:数据类型
- not null:指定变量非空
- : = 给变量或是常量指定初始值
- default:指定初始值
- expr:指定初始值的pl/sql表达式,可是文本值、其他变量、函数等
2)案例
-
--定义一个变长字符串 v_ename varchar2(10); --定义一个小数,范围-9999.99~9999.99 v_sal number(6,2); --定义一个小树,并赋初始值为5.4 v_sal2 number(6,2):=5.4; --定义一个日期类型的数据 v_hiredate date; --定义一个布尔变量,不能为空,初始值为false; v_valid boolean not null default false;
3)使用
-
--输入员工号,显示雇员姓名、工资、个人所得税(税率为0.03) declare c_tax_rate number(3,2):=0.03;--税率 v_ename varchar2(5);--用户名 v_sal number(7,2);--工资 v_tax_sal number(7,2);--所得税 begin --执行部分 select ename,sal into v_ename,v_sal from emp where empno=&no; --计算所得税 v_tax_sal:=v_sal*c_tax_rate; --输出 dbms_output.put_line(\'姓名:\'||v_ename||\',工资:\'||v_sal||\',个人所得税为:\'||v_tax_sal); end; /
4)使用%type类型
对于上面的pl/sql块:如果员工的姓名超过了5字符的话,就会有错误。为了降低pl/sql程序的维护工作量,可以使用%type属性定义变量。这样,可以按照数据库列来确定定义的变量的类型和长度。
-
--语法 标识符名 表名.列名%type; --如
v_ename varchar2(5); --用户名(上面的写法) v_ename emp.ename%type; --用户名(改进的写法)
3、复合变量(composite)
复合变量是用于存放多个值的变量。
1)类型
- pl/sql记录
- pl/sql表
- 嵌套表
- varray(动态表)
2)pl/sql记录
相当于高级语言中的结构体、类。
注意:当引用pl/sql记录成员时,必须要加记录变量作为前缀(记录变量.记录成员)。
-
--pl/sql记录实例 declare --定义一个pl/sql记录类型emp_record_type,类型包含三个数据name,salary,title type emp_record_type is record( name emp.ename%type, salary emp.sal%type, title emp.job%type); --定义了一个变量 sp_record ,类型是emp_record_type sp_record emp_record_type; begin select ename,sal,job into sp_record from emp where empno=7788; dbms_output.put_line(\'员工名:\'||sp_record.name||\',工资是:\'||sp_record.salary||\',工作是:\'||sp_record.title); end; /
3)pl/sql表
相当于高级语言中的数组。注意:在高级语言中数组的下标不能为负数,而pl/sql的下标是可以为负数的,并且表元素的下标没有限制。
-
--pl/sql表实例 declare --定义了一个pl/sql表类型sp_table_type,该类型是用于存放emp.ename%type --index by binary_integer表示下标为整数 type sp_table_type is table of emp.ename%type index by binary_integer; --定义了变量sp_table,类型是sp_table_type sp_table sp_table_type; begin select ename into sp_table(0) from emp where empno=7788; dbms_output.put_line(\'员工名\'||sp_table(0)); end; /
注:sp_table_type是pl/sql表类型;emp.ename%type制定了表的元素的类型和长度;sp_table为pl/sql表变量;sp_table(0)表示下表为0的元素。如果去掉where条件:
-
--pl/sql表实例 declare --定义了一个pl/sql表类型sp_table_type,该类型是用于存放emp.ename%type --index by binary_integer表示下标为整数 type sp_table_type is table of emp.ename%type index by binary_integer; --定义了变量sp_table,类型是sp_table_type sp_table sp_table_type; begin select ename into sp_table(0) from emp; dbms_output.put_line(\'员工名\'||sp_table(0)); end; /
4、参照变量
1)概念
参照变量是用于存放数值指针的变量。是应用程序共享相同对象,从而降低占用的空间。分为:游标变量(ref cursor)和对象类型变量(ref obj_type)。
2)游标变量(ref cursor)
定义游标时,不需要指定相应的select语句。使用游标(open)时,需要制定select语句。
实例:使用pl/sql编写一个块,可以输入部门号,并显示该部门所有员工姓名和工资。
-
--使用pl/sql编写一个块,可以输入部门号,并显示该部门所有员工姓名和工资。 declare --定义游标类型 type sp_emp_cursor is ref cursor; --定义一个游标变量 test_cursor sp_emp_cursor; --定义变量 v_ename emp.ename%type; v_sal emp.sal%type; begin --执行 --把test_cursor和一个select结合 open test_cursor for select ename,sal from emp where deptno=&no; --循环取出 loop fetch test_cursor into v_ename,v_sal; --判断是否test_cursor为空 exit when test_cursor%notfound; dbms_output.put_line(\'姓名:\'||v_ename||\',薪水:\'||v_sal); end loop; end; /
在上面的基础上,如果某个员工的工资低于200元,则增加100元。
-
--使用pl/sql编写一个块,可以输入部门号,并显示该部门所有员工姓名和工资。 declare --定义游标类型 type sp_emp_cursor is ref cursor; --定义一个游标变量 test_cursor sp_emp_cursor; --定义变量 v_ename emp.ename%type; v_sal emp.sal%type; begin --执行 --把test_cursor和一个select结合 open test_cursor for select ename,sal from emp where deptno=&no; --循环取出 loop fetch test_cursor into v_ename,v_sal; --判断工资高低,决定是否更新 if v_sal<200 then update emp set sal=sal+100 where ename=v_ename; end if; fetch test_cursor into v_ename,v_sal; --判断是否test_cursor为空 exit when test_cursor%notfound; dbms_output.put_line(\'姓名:\'||v_ename||\',薪水:\'||v_sal); end loop; end; /
以上是关于Oracle实战笔记(第六天)之PL/SQL基础的主要内容,如果未能解决你的问题,请参考以下文章