plsql笔记存储过程
2026/8/29 7:05:55 网站建设 项目流程


游标 cursor

定义 :游标 实际上是一个指针 指向 结果集的每一行数据,初始的时候 指向第一行数据

作用 处理多行数据 ------for select


语法

declare
cursor 游标名 is select语句;-------1 声明游标

begin
open 游标名;-----------------------2 打开游标

fetch 游标名 into 变量1 ,变量2......;---3. 使用游标 提取游标****

----(1) 提取数据 (2) 交给变量 (3) 指针下移

close 游标名; ---------------------4 关闭游标
end;


select * from emp


例题 打印输出 emp表中所有员工的姓名 岗位

declare
cursor c1 is select ename,job from emp ; -----1
v_ename emp.ename%type;
V_job emp.job%type;
begin
open c1 ; -----------2

fetch c1 into v_ename,V_job; ----3
dbms_output.put_line(v_ename||V_job);

close c1; -----------------4

end;


declare
cursor c1 is select ename,job from emp ; -----1
v_ename emp.ename%type;
V_job emp.job%type;
begin
open c1 ; -----------2

fetch c1 into v_ename,V_job; ----3
dbms_output.put_line(v_ename||V_job);

close c1; -----------------4

end;


注意 游标 是需要配合 循环来使用的


loop

例题 打印输出 emp表中所有员工的姓名 岗位


declare
cursor c1 is select ename,job from emp ; ------1
v_ename varchar2(20);
V_job emp.job%type;
begin
open c1 ; -------------------------------------2

loop

fetch c1 into V_ename,V_job ; -------------------3

exit when c1%notfound;

dbms_output.put_line(V_ename||v_job);




end loop;

close c1;
end;


游标的四个属性

1. 游标名%found --------------游标有值的时候返回 true

2. 游标名%notfound ------------游标没有值的时候 返回 true

3. 游标名%isopen -------判断游标是否打开 如果是 则返回 true

4. 游标名%rowcount -------统计游标处理的行数 ------返回的是数值


练习
例题 打印输出 emp表中所有员工的姓名 岗位
--while

declare
cursor c1 is select ename,job from emp ;
V_ename emp.ename%type;
V_job emp.job%type;

begin
open c1 ;
fetch c1 into V_ename,V_job ;

while c1%found ----------游标有值的时候
loop
dbms_output.put_line(V_ename||V_job);


fetch c1 into V_ename,V_job ;

end loop;
close c1;
end;


for 循环配合游标使用

1.for 循环会 自动的打开和关闭游标

2. for 循环会 自动的 fetch 游标

例题 打印输出 emp表中所有员工的姓名 岗位
--for

declare
cursor c1 is select ename,job from emp ;----1 声明游标

begin
for i in c1 ---游标
loop
dbms_output.put_line(i.ename||i.job);


end loop;


end;


等价写法


declare

begin
for i in (select ename,job from emp)
loop
dbms_output.put_line(i.ename||i.job);


end loop;


end;

------------------------------------

练习

打印输出 员工的姓名岗位薪资部门编号部门名称以及部门平均工资

--必须使用游标 ---3种方法 loop while for

--------------------------------------------------------------------
有名块 :有名字的 ,是可以永久保存到数据库中随时拿来调用 比如 to_date


函数 function
--特点 函数 有 且只有一个返回值

自定义函数

语法

create [or replace] function 函数名[(形参1 形参类型,形参2 形参类型....)]

return 返回值的类型


------------------------------------------------以上的所有类型 不能写长度
is|as
--声明部分

begin
--执行部分 核心部分 实现函数的过程的部分

return 最终的值 ; ----函数最终的返回结果

end ;

例题 创建没有参数的函数, 返回上个月的最后一天

create or replace function fu_97 --------------------名字 有意义

return date ----返回值的类型

is
V_d date; -----变量

begin

select add_months(last_day(sysdate),-1)
into V_d
from dual;

return v_d;

end;

---有名块

select fu_97 -----使用函数的时候 里面的参数的个数 顺序 属性要和创建时形参一直

from dual



例题 :创建一个有参数的函数 要求 返回任意一个日期的上个月的最后一天

create or replace function fu_97( v_d date ) ---形参

return date

is
V_a date; ----变量


begin
select add_months(last_day( V_d ) ,-1) into V_a from dual;

return v_a;


end;


select fu_97( to_date('2000/3/1','yyyy/mm/dd') ) from dual;


练习 创建一个函数要求 传入一个员工编号 返回该员工的部门的平均工资

create or replace function fu_97(v_empno number)
return number

is
v_deptno number;
V_avg number;
begin
select deptno into V_deptno from emp where empno=V_empno;

select avg(sal) into V_avg from emp where deptno=V_deptno;

return v_avg;

end;

select ename,fu_97(7566) from emp

select fu_97(7566) from dual;

CREATE or replace FUNCTION fu_avg_sal(V_d number)

return NUMBER

is
V_a number;
BEGIN
SELECT AVG(b.sal)
into V_a
FROM emp a
INNER JOIN emp b
on a.deptno = b.deptno
where a.empno =v_d;

return V_a;
end;


SELECT fu_avg_sal(7566) FROM dual;

练习 创建一个函数 传入一个员工编号
如果该员工的工资等级是 1 则返回 低等级 2-3 中等级 4-5 高等级


create or replace function fu_97 (v_empno number)
return varchar2

is
V_grade number;

begin
select grade
into V_grade
from emp
left join salgrade
on sal between losal and hisal
where empno=V_empno;

if v_grade =1 then
return '低等级';
elsif v_grade between 2 and 3 then
return '中等级';
elsif v_grade in(4,5) then
return '高等级';
end if;

end;


select fu_97(7566) from dual;


----------------------------------------------------
存储过程 --有名块 ---数据库对象之一
是将 任务 语句 存储起来 ,随时拿来调用

---函数 :有且只有一个返回值 ----返回值

---存储过程: 把过程存储起来 ----没有返回值

创建存储过程语法

create [or replace ] procedure (形参1 形参类型,形参2 形参类型)


------------------以上类型不能写长度

is

begin

end;


例题 创建一个存储过程 传入一个员工编号 打印输出该员工的姓名

create or replace procedure sp_97(v_empno number)

is
V_ename emp.ename%type;

begin
select ename into V_ename from emp where empno=V_empno;
dbms_output.put_line(V_ename);

end;


调用存储过程

1. call 存储过程();

2. 用 程序块 调用存储过程

declare

begin
存储过程(); ----调用存储过程

end;

call sp_97(7566); --调用的时候 参数的个数顺序属性和创建时一致


declare


begin
sp_97(7839);

end;

练习 创建一个存储过程 传入 员工编号姓名岗位薪资入职日期以及部门编号

要求 将传入的参数 insert 插入到 emp表中

create or replace procedure sp_97(V_empno number
,V_ename varchar2
,V_job varchar2
,V_hiredate date
,V_sal number
,V_deptno emp.deptno%type)

is


begin

insert into emp (empno,ename,job,sal,deptno,hiredate)
values (V_empno,V_ename,V_job,V_sal,V_deptno,V_hiredate);

end;

call sp_97(3344,'马德华','猪八戒',sysdate,1,10)


select * from emp

练习 :
1 创建一个函数函数函数 要求 传入部门编号 返回该部门的平均工资

create or replace function fu_97(V_deptno number)
return number
is
V_avg number;
begin
select avg(sal) into V_avg from emp where deptno=V_deptno;
return V_avg;
end;

select fu_97(10) from dual;

2.创建一个存储过程 传入一个员工编号 如果 该员工的工资 高于自己部门平均工资
则降薪200 低于 涨薪200 等于 不变
要求 1. 必须利用第一题的函数 2. 打印输出涨薪 前后的薪资

create or replace procedure sp_97(v_empno number)

is
V_sal number;
V_d number;
V_sal1 number;
begin
select sal,deptno into V_sal,V_d from emp where empno=V_empno;


if V_sal > fu_97(v_d ) then
update emp set sal=sal-200 where empno=V_empno returning sal into V_sal1;
elsif V_sal < fu_97(v_d) then
update emp set sal=sal+200 where empno=V_empno returning sal into V_sal1;
else
null; --什么都不做
end if;

dbms_output.put_line(v_sal || v_sal1);

end;


call sp_97(7566);

----------------------------------------------------------

存储过程的三种形参

1 输入型形参 in ----默认

2. 输出型形参 out

3. 输入输出型形参 in out


1 输入型形参 in ----默认

create or replace procedure sp_97(v_empno [in] number)

is
V_sal number;
V_d number;
V_sal1 number;
begin
select sal,deptno into V_sal,V_d from emp where empno=V_empno;


if V_sal > fu_97(v_d ) then
update emp set sal=sal-200 where empno=V_empno returning sal into V_sal1;
elsif V_sal < fu_97(v_d) then
update emp set sal=sal+200 where empno=V_empno returning sal into V_sal1;
else
null; --什么都不做
end if;

dbms_output.put_line(v_sal || v_sal1);

end;

2. 输出型形参 out

例题 创建一个存储过程 插入一个员工编号 传出一个员工姓名

--例题 创建一个存储过程 传入一个员工编号 打印输出该员工的姓名

create or replace procedure sp_97( V_empno in number ,V_ename out varchar2 )

is

begin
select ename into V_ename from emp where empno=v_empno;

end;


call sp_97( 7566,变量 ); ------不能用call 调用

declare
a varchar2(20);
begin
sp_97(7566 , a );---接收了 返回的姓名 a

dbms_output.put_line(a);---a 是变量



end;


--可以通过 out 型形参 返回值 -----存储过程也可以有返回值


存储过程和函数区别

函数有且只有一个返回值 ,存储过程可以通过 out 输出型形参 有多个返回值


练习 传入员工编号 传入该员工的工资等级

create or replace procedure sp_97(V_empno in number,v_grade out number)

is


begin
select grade into V_grade
from emp
left join salgrade
on sal between losal and hisal
where empno=V_empno;

end;

declare
a number;
begin
sp_97(7566,a);

dbms_output.put_line(a);

end;

3 输入输出型形参 in out --了解

例题传入员工编号 输出该员工的姓名

create or replace procedure sp_97( v_a in out emp%rowtype )

is

begin
select ename into v_a.ename from emp where empno=v_a.empno;

end;


declare
v_b emp%rowtype; ------V_a 个数 顺序 属性 完全一致

begin
v_b.empno:=7566;

sp_97( v_b );

dbms_output.put_line(v_b.ename);

end;


------------------------------------------------存储过程结束


# Oracle PL/SQL 练习题(10道,中等难度)
约束要求:
1. 允许:存储过程、自定义函数、`IF`判断、`CASE`、`FOR`/`WHILE`循环
2. 禁止:触发器、异常处理块(`EXCEPTION`)
3. 环境:基于经典`emp`、`dept`表,题目可直接在SCOTT用户下运行,不需要自建业务表

> 说明:函数必须有返回值;存储过程无返回值,可使用IN/OUT参数;不许写`EXCEPTION`部分。

## 题目1(存储过程‑IF判断)
编写存储过程`p_check_sal`,传入员工编号`p_empno`。
查询该员工工资:
- 工资大于3000:输出`员工XXX工资偏高`
- 工资1500~3000:输出`员工XXX工资正常`
- 小于1500:输出`员工XXX工资偏低`
要求使用DBMS_OUTPUT打印结果。

## 题目2(函数‑IF)
编写函数`f_get_job_level`,接收岗位`p_job`,返回岗位等级数字:
- 'PRESIDENT' → 1
- 'MANAGER' →2
- 'ANALYST' →3
- 其余岗位返回4。

## 题目3(存储过程‑WHILE循环)
编写存储过程`p_print_num`,传入数字`p_n`,使用**WHILE循环**,打印1~p_n之间所有偶数。

## 题目4(函数‑FOR循环)
编写函数`f_sum_even`,接收入参`p_max`,使用FOR循环,计算1~p_max所有偶数之和,返回总和。

## 题目5(存储过程‑IF + 查询 + OUT参数)
创建存储过程`p_dept_stats`,入参部门编号`p_deptno`,两个OUT参数:`o_emp_count`(部门人数)、`o_avg_sal`(部门平均工资)。
逻辑:
如果部门人数大于5,则把平均工资上浮10%赋值给o_avg_sal;否则保持原平均工资。

## 题目6(函数‑CASE判断)
编写函数`f_sal_tax`,传入工资`p_sal`,使用CASE表达式计算模拟个税并返回:
- sal<=1000:扣税0
- 1000<sal<=2000:扣5%
- 2000<sal<=3500:扣10%
- sal>3500:扣15%
返回扣税金额。

## 题目7(存储过程‑FOR循环 + IF嵌套)
存储过程`p_sal_update_loop`,传入部门号`p_deptno`。
遍历该部门全部员工(FOR循环游标for):
- 如果岗位是'MANAGER',工资增加200;
- 如果岗位是'CLERK',工资增加100;
其他岗位工资不变。执行update更新表。

> 提示:使用`FOR rec IN (select empno,job,sal from emp where deptno=p_deptno) LOOP`,禁止显式声明cursor。

## 题目8(函数‑循环+判断)
编写函数`f_count_high_sal`,入参部门编号`p_deptno`,统计该部门工资大于2500的员工人数,返回统计数量。使用FOR循环遍历,不允许直接count聚合一步返回结果(必须循环逐个判断计数)。

## 题目9(存储过程‑多条件IF,OUT输出字符串)
存储过程`p_emp_info`,输入员工编号`p_empno`,输出OUT字符串`o_result`。
拼接信息:`姓名:xxx,岗位:xxx`;
附加规则:
- 入职早于1982年,追加`[老员工]`
- 工资>2800,追加`[高薪]`。

## 题目10(综合:函数调用存储过程,IF+循环)
1. 复用第6题函数`f_sal_tax`;
2. 创建存储过程`p_show_tax_list(p_deptno number)`;
使用FOR循环遍历该部门所有员工,调用`f_sal_tax`得到每个人扣税,DBMS_OUTPUT打印:`姓名:xxx,工资:xxx,扣税:xxx`。

---

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询