Oracle_PL/SQL(4) 过程和函数
create table s_sc (
SNAME VARCHAR2(20) primary key,
c_grade NUMBER(6),
m_grade NUMBER(6),
e_grade NUMBER(6)
);
declare
begin
for rs in (select sno,sname from student) loop
insert into s_sc (sname) values (rs.sname);
for rs2 in (select grade,cname from sc,course
where sc.cno=course.cno and sno=rs.sno) loop
if rs2.cname='语文' then
update s_sc set C_GRADE=rs2.grade where sname=rs.sname;
elsif rs2.cname='数学' then
update s_sc set M_GRADE=rs2.grade where sname=rs.sname;
elsif rs2.cname='英语' then
update s_sc set E_GRADE=rs2.grade where sname=rs.sname;
end if;
end loop;
end loop;
end;
开发子程序:过程和函数
过程:存储过程是用于执行特定操作的PL/SQL块。特定操作主要包括对数据库表的增、删、改、查。
如果系统或用户经常需要执行特定操作,那么可以考虑基于这些操作建立存储过程,不仅可以简化应用的开发和维护,还可以提高应用程序的运行效率。
过程:用于对表的增、删、改、查
函数:用于返回特定数据
1.过程
语法:
create [or replace] procedure proc_name(
argument1 [model] datatype1,arguement2 [mode2],...)
is [as]
变量
begin
语句
end;
关键字解释:
create or replace:create是创建,replace是替换,
create or replace 如果系统中不存在这个存储过程时,则创建一个新的;
如果系统中已经存在同名的存储过程,则将原来的存储过程覆盖或替换掉。
procedure:存储过程的关键字,类似的关键字还有trigger、function、package等。
proc_name:存储过程名称,在同一个用户/模式下唯一,命名规则为:proc_表名_操作名。
参数名:有意义的名字,不能是a\bc\d等,一般是列名。
mode:参数模式,包括IN、OUT、INOUT三种类型。
IN仅作为输入参数使用,OUT仅作为输出参数使用,INOUT即是输入参数又是输出参数。
is …begin间:是定义变量的区域。
begin … end间:是存储过程的主体区域,用来完成存储过程的功能。
1.1 建立过程:不带任何参数
第一个存储过程,无参数部分、无变量、语句部分只有null
create or replace procedure proc_null
is
begin
null;
end proc_null;
不能一句语句都没有
不能重名
练习:构造无参存储过程打印当前日期
例1:
create or replace procedure proc_sysdate is
begin
--dbms_session.set_nls('nls_date_format','"yyyy-mm-dd hh24:mi:ss"');
dbms_output.put_line(sysdate);
end proc_sysdate;
/
show error;
1.2 调用过程
Command窗口调用
set serveroutput on;
exec proc_sysdate;
execute proc_sysdate;
SQL窗口、Test窗口
begin
proc_sysdate();
end;
总结:
存储过程可以被pl/sql块(包括:触发器、存储过程、包、函数、job)直接调用;
也可以被sql通过执行命令execute(缩写exec)调用;
ORM框架(mybais、ibatis、hibenate)及JDBC将sql语句作为字符串进行传递,
所以sql中仍可以通过execute执行存储过程。
1.3 建立过程:带有IN参数
例2:
create or replace procedure add_employee
(p_eno number,p_name varchar2,p_sal number,
p_job varchar2 default 'clerk',p_dno number)
is
begin
insert into emp(empno,ename,sal,job,deptno) values(p_eno,p_name,p_sal,p_job,p_dno);
end;
/
show err;
exec add_employee(1111,'clark',2000,'manager',10);
练习:编写proc_dept_add存储过程
create or replace procedure proc_dept_add
(p_deptno number,p_dname varchar2,p_loc varchar2)
is
begin
insert into dept(deptno,dname,loc) values(p_deptno,p_dname,p_loc);
end;
/
show err
1.4 建立过程:带有OUT参数
例3:
create or replace procedure proc_employee
(p_eno number,p_name out varchar2,p_salary out number)
is
begin
select ename,sal into p_name,p_salary from emp where empno=p_eno;
end;
/
show err;
当在应用程序中调用该过程时,必须要定义变量接受输出参数的数据
sql>variable name varchar2(10);
var salary number;
exec proc_employee(7788,:name,:salary);
declare
v_name varchar2(100);
v_salary emp.sal%type;
begin
proc_employee(7788,v_name,v_salary);
dbms_output.put_line(v_name||' '||v_salary);
end;
1.5 建立过程:带有IN OUT参数(输入输出参数)
例4:
create or replace procedure proc_compute
(p_num1 in out number,p_num2 in out number)
is
v1 number;
v2 number;
begin
v1:=trunc(p_num1/p_num2);
v2:=mod(p_num1,p_num2);
p_num1:=v1;
p_num2:=v2;
end;
/
show err;
sql>variable n1 number
var n2 number
exec :n1:=100
exec :n2:=30
exec proc_compute(:n1,:n2)
declare
v_num1 number:=100;
v_num2 number:=30;
begin
proc_compute(v_num1,v_num2);
dbms_output.put_line('商:'||v_num1||' 余数:'||v_num2);
end;
1.6 为参数传递变量和数据
位置传递,名称传递,组合传递三种
位置传递:在调用子程序时按照参数定义的顺序为参数指定相应的变量或数值
exec proc_dept_add(41,'开发部','北京');
--exec proc_dept_add(42);
exec proc_dept_add(42,null,null);
create or replace procedure proc_dept_add
(p_deptno number,p_dname varchar2 default '',p_loc varchar2 default '')
is
begin
insert into dept(deptno,dname,loc) values(p_deptno,p_dname,p_loc);
end;
/
show err;
exec proc_dept_add(43);
名称传递:在调用子程序时指定参数名,并使用关联符号=>为其提供相应的数值或变量
exec proc_dept_add(p_dname=>'售后部',p_deptno=>44);
exec proc_dept_add(p_deptno=>45);
组合传递:同时使用位置传递和名称传递
exec proc_dept_add(46,p_loc=>'深圳');
exec proc_dept_add(47,p_dname=>'人事部',p_loc=>'上海');
1.7 查看过程原代码
oracle会将过程名,源代码以及其执行代码存放到数据字典中.执行时直接按照其执行代码执行
可查询数据字典(user_source)
select text from user_source where lower(name)='proc_dept_add';
1.8 删除过程
drop procedure proc_name;
1.9 编译存储过程
alter procedure proc_name compile;
--alter table account add mark varchar2(100); 更新表结构会导致存储过程失效
练习:
转账业务
create table account(
accountid number(16),
name varchar2(20),
value number(16,2));
insert into account values(111,'acdd',10000);
insert into account values(112,'acdd1',100);
create or replace procedure proc_trans_monery
(p_account_out number,p_account_in number,p_value number)
is
begin
Update account set value= value-p_value where accountid= p_account_out;
Update account set value= value+p_value where accountid= p_account_in;
end;
exec proc_trans_monery(111,112,1000);
做到这步基本功能是完成,但是代码里有很多bug,达不到系统使用的标准。
首先存在的问题有:转出账户是否存在是否存在?转出账户的的金额是否够转出?转入账户是否存在?
要解决这些问题需要增加业务校验逻辑,接下来我们改造我们的存储过程。
create or replace procedure proc_trans_value(p_acid_out number,p_acid_in number,p_value number) is
l_cnt number(8):=0;
l_value account.value%type;
begin
select count(1) into l_cnt from account where accountid=p_acid_out;
if l_cnt=1 then
select value into l_value from account where accountid=p_acid_out;
if l_value>=p_value then
update account set value=value-p_value where accountid=p_acid_out;
else
raise_application_error(-20001,'[转出账户金额不足]');
end if;
else
raise_application_error(-20001,'[转出账户不存在]');
end if;
select count(1) into l_cnt from account where accountid=p_acid_in;
if l_cnt=1 then
update account set value=value+p_value where accountid=p_acid_in;
else
raise_application_error(-20001,'[转入账户不存在]');
end if;
end proc_trans_value;
用户自定义异常
raise_application_error(-20001,'[转入账户不存在]');
参数1:错误号,范围:-20000到-20999
参数2:错误消息
作业
1.扩展转账业务,增加一个操作日志表(序号,账户,操作,金额,时间),
当账户表发生改变时增加操作日志表信息。
2.按照部门(scott用户下dept表)经营情况给职员(scott用户下emp表)调薪,
调薪幅度为ACCOUNTING 5%, RESEARCH 7%, SALES 8%, OPERATIONS 6%.
2.函数
用于返回特定的值
语法:
create [or replace] function func_name
(argument1 [mode1] datatype1,
argument2 [mode2] datatype2,
.....) return datatype --函数头部必须要带有RETURN子句,至少要包含一条RETURN语句
is|as
pl/sql block;
2.1 建立函数:不带任何参数
例1:
create or replace function func_user return varchar2
is
v_user varchar2(100);
begin
select username into v_user from user_users;
return v_user;
end;
/
create or replace function func_userid return number
is
v_userid number;
begin
select user_id into v_userid from user_users;
return v_userid;
end;
/
2.2 函数调用
使用变量接受函数返回值
在Command窗口调用
sql>var v1 varchar2(100);
exec :v1:=func_user;
print v1;
在SQL语句中直接调用函数
select func_user from dual;
使用DBMS_OUTPUT调用函数
set serveroutput on;
exec dbms_output.put_line('当前数据库用户:'||func_user);
总结:函数主要是通过SQL进行调用的。
2.3 建立函数:带有IN参数
例2:
create or replace function func_emp_sal(p_ename in varchar2) return number
is
v_result emp.sal%type;
begin
select sal into v_result from emp where ename=upper(p_ename);
return v_result;
end func_emp_sal;
/
show error;
--在函数中select into语句不会报未找到数据的错误。
select func_emp_sal('scott') from dual;
select ename,func_emp_sal(ename) from emp;
练习:用函数实现输入部门号输出部门名。
select func_dname(10) from dual;
select emp.*,func_dname(deptno) dname from emp;
2.4 建立函数:带有out参数
例3:
create or replace function func_dept_dname(p_ename varchar2,
p_job out varchar2) return varchar2
as
v_dname dept.dname%type;
begin
select a.job,b.dname into p_job,v_dname from emp a,dept b
where a.deptno=b.deptno and upper(a.ename)=upper(p_ename);
return v_dname;
end;
/
show err;
不能使用SQL调用
sql>var job varchar2(20);
--select func_dept_dname('scott',:job) from dual;
在command窗口内调用
sql>var job varchar2(20);
var dname varchar2(20);
exec :dname:=func_dept_dname('scott',:job);
在匿名块内调用
declare
v_dname dept.dname%type;
v_job emp.job%type;
begin
v_dname:=func_dept_dname('scott',v_job);
dbms_output.put_line('部门名:'||v_dname||' 职位:'||v_job);
end;
--输出2个值
create or replace function func_dname_job2(
p_ename in varchar2) return varchar2
is
v_rs varchar2(100);
begin
select dname||','||job into v_rs from emp,dept
where emp.deptno=dept.deptno and ename=upper(p_ename);
return v_rs;
end func_dname_job2;
/
show error;
--全部输出
select func_dname_job2('scott') str from dual;
--取分隔符位置
select instr(func_dname_job2('scott'),',') from dual;
--取部门信息
select substr(str,1,instr(str,',')-1)
from (select func_dname_job2('scott') str from dual);
--取职位信息
select substr(str,instr(str,',')+1)
from (select func_dname_job2('scott') str from dual);
练习:拼接同一个部门下的职员名
create or replace function func_emp_ename(p_deptno number) return varchar2
is
v_ename varchar2(100):='';
begin
for rs in (select ename from emp where deptno=p_deptno) loop
v_ename:=v_ename||' '||rs.ename;
end loop;
v_ename:=substr(v_ename,2);
return v_ename;
end;
/
2.5 建立函数:带有IN OUT参数
例4:
create or replace function func_inout(
p_num1 number,p_num2 in out number) return number
is
v_rs number;
begin
v_rs:=trunc(p_num1/p_num2);
p_num2:=mod(p_num1,p_num2);
return v_rs;
end func_inout;
/
show error;
在command窗口内调用
sql>var result1 number;
var result2 number;
exec :result2:=30;
exec :result1:=func_result(100,:result2);
在匿名块内调用
declare
v_shang number;
v_yushu number;
begin
v_yushu:=5;
v_shang:=func_inout(11,v_yushu);
dbms_output.put_line('商:'||v_shang||' 余数:'||v_yushu);
end;
2.6 查看函数源代码
oracle会将函数名及其源代码信息存放到数据字典中user_source
set pagesize 40
select text from user_source where lower(name)='func_result';
2.7.删除函数
drop function func_name;
2.8 编译函数
alter function func_name compile;
2.9 函数调用限制
SQL只能调用带有输入参数,不能带有输出,输入输出函数
SQL不能调用使用返回值是PL/SQL特有数据类型(boolean,table,record等)的函数
SQL语句中调用的函数不能包含INSERT,UPDATE和DELETE语句
作业:
年份转大写:要求2017年输出“二0一七”
月份转大写:要求9月输出“九”,12月输出“十二”
日转大写: 要求9日输出“九”,31日输出“三十一”
金额转大写:要求10234567.89 输出“壹仟零贰拾叁万肆仟伍佰陆拾柒圆零捌角九分”
3. 管理子程序
3.1 列出当前用户的子程序
数据字典视图USER_OBJECTS用于显示当前用户所包含的所有对象.
(表,视图,索引,过程,函数,包)
sql>col object_name format a20
select object_name,created,status from user_objects
where lower(object_type) in ('procedure','function');
3.2 列出子程序源代码
select text from user_source where name='PROC_DEPT_ADD';
3.3 列出子程序编译错误
使用SHOW ERRORS命令确定错误原因和位置
show errors procedure PROC_DEPT_ADD
使用数据字典视图USER_ERRORS确定错误原因和位置
col text format a50
select line||'/'||position as "line/col",text error from user_errors
where name='PROC_DEPT_ADD';
3.4 列出对象依赖关系
使用数据字典视图USER_DEPENDENCIES确定直接依赖关系
select name,type from user_dependencies where referenced_name='EMP';
使用工具视图DEPTREE和IDEPTREE确定直接依赖和间接依赖关系
先运行SQL脚本UTLDTREE.SQL来建立这两个视图和过程DEPTREE_FILL,然后调用DEPTREE_FILL填充这两个视图
sql>@%oracle_home%\rdbms\admin\utldtree
exec deptree_fill('TABLE','scott','emp')
执行后会将直接或间接依赖于SCOTT.EMP表的所有对象填充到视图DEPTREE和IDEPTREE中.
select nested_level,name,type from deptree;
select * from ideptree
3.5 重新编译子程序
当修改了被引用对象的结构时,就会将相关依赖对象转变为无效(INVALID)状态。
alter table emp add remark varchar2(10);
select object_name,object_type,status from user_objects where lower(status)='invalid';
为了避免子程序的运行错误,应该重新编译这些存储对象
alter procedure add_employee compile;
alter view dept10 compile;
alter function FUNC_EMP_SAL compile;
Oracle_PL/SQL(4) 过程和函数的更多相关文章
- C#函数与SQL储存过程
一点点小认识作为memo,求指正. C#的函数与SQL的储存过程有很多的相似性, 它们都是一段封闭的代码块,来提高代码的重用性,虽然现在复制粘贴很方便,但是我们在写多个函数的时候频繁的复制粘贴相同的内 ...
- oracle PL/SQL(procedure language/SQL)程序设计之函数+过程+包
匿名PL/SQL块回顾 DECLARE (可选) 定义在PL/SQL块中要使用的对象BEGIN (必须) 执行语句EXCEPTION (可选) 错误处理语句END; (必须)匿名块( ...
- Oracle数据库之PL/SQL过程与函数
Oracle数据库之PL/SQL过程与函数 PL/SQL块分为匿名块与命名块,命名块又包含子程序.包和触发器. 过程和函数统称为PL/SQL子程序,我们可以将商业逻辑.企业规则写成过程或函数保存到数据 ...
- oracle PL/SQL(procedure language/SQL)程序设计之函数+过程+包(转)
匿名PL/SQL块回顾 DECLARE (可选) 定义在PL/SQL块中要使用的对象 BEGIN (必须) 执行语句 EXCEPTION (可选) 错误处理语句 END; (必 ...
- ORACLE PL/SQL编程之六:把过程与函数说透(穷追猛打,把根儿都拔起!)
原文:ORACLE PL/SQL编程之六:把过程与函数说透(穷追猛打,把根儿都拔起!) ORACLE PL/SQL编程之六: 把过程与函数说透(穷追猛打,把根儿都拔起!) 继上篇:ORACLE P ...
- PL/SQL规范、块、过程、函数、包、触发器
1.pl/sql规范 标识符号的命名规范 1) 定义变量,用 v- 作为前缀 v-sal 2)定义常亮, 用 c- 作为前缀 c-rate 3) 定义游标,用 cursor作为后缀 emp_curso ...
- sql server中常用方法函数
SQL SERVER常用函数 1.DATEADD在向指定日期加上一段时间的基础上,返回新的 datetime 值. (1)语法: DATEADD ( datepart , number, date ) ...
- 【SQL】CLR聚合函数什么鬼
之前写过一个合并字符串的CLR聚合函数,基本是照抄MS的示例,外加了一些处理,已经投入使用很长时间,没什么问题也就没怎么研究,近日想改造一下,遇到一些问题,遂捣鼓一番,有些心得,记录如下. 一.杂项 ...
- Oracle过程及函数的参数模式,In、out、in out模式
Oracle过程及函数的参数模式 In.out.in out模式 在Oracle中过程与函数都可以有参数,参数的类型可以指定为in.out.in out三种模式. 三种参数的具体说明,如下图所示: ( ...
随机推荐
- Delphi 透明窗体显示文字
设置窗体属性:BorderStyle 属性设置为 bsNoneColor 属性设置为 clWhite(白色:窗体背景色)TransparentColor 属性设置为 trueTransparentCo ...
- scala spark 聚类
import org.apache.spark.ml.clustering.KMeansimport org.apache.spark.ml.evaluation.ClusteringEvaluato ...
- CodeForces-1132C-Painting the Fence-(前缀和)
You have a long fence which consists of nn sections. Unfortunately, it is not painted, so you decide ...
- Java8 parallelStream与迭代器Iterator性能
定义一个测试类 public class TestParallelStream { private List<Integer> list; private int size; privat ...
- java开始的笔记
这几天第一写java的代码有些东西不是很明白: java的输入跟c/c++有点不一样,他的输入都是要首先创建每一个变量的内存,并且输入的类型不同. 就像string的那样. Scanner s=new ...
- Building and using plug-ins for Android
[Building and using plug-ins for Android] 1.AAR plug-ins and Android Libraries Android Archive (AAR) ...
- 查询结果null替换为0
xxx表示字段名 mysql数据库 : ifnull( xxx , 0 ) oracle数据库: NVL(xxx , 0 )
- django实现分页
分页实现思路: 1.接口拉取库内数据,进行分页 2.页面实现分页组件,切换不同链接 一.接口实现: from django.core.paginator import Paginator p=Pagi ...
- Forms.WebBrowser与Controls.WebBrowser区别
Forms.WebBrowser与Controls.WebBrowser区别 Forms.WebBrowser有ScrollBarsEnabled 属性,即窗口滚动条,可以设置为false即可: Co ...
- 手机端用swiper组件 轮播图设置后右侧出现空白 及 部分手机浏览器打开网页空白
我的方法是设置内容css overflow:hidden;width:100%; ok. 之前搜到一个方法也可以,就是设置css height: auto;overflow-y: scroll; 但是 ...