文章目录
基础语法
plsql的HelloWorld
set serveroutput on
--打印Hello World
declare
--说明部分(变量,光标或者例外说明)
begin
--程序体(DML语句)
dbms_output.put_line('Hello World');
end;
基本变量类型的使用
-- 使用基本变量类型
declare
--定义基本变量类型
--基本数据类型
pnumber number(7,2);
--字符串变量
pname varchar(20);
--日期变量
pdate date;
begin
pnumber := 1;
DBMS_OUTPUT.PUT_LINE(pnumber);
pname := 'Tom';
DBMS_OUTPUT.PUT_LINE(pname);
pdate := sysdate;
DBMS_OUTPUT.PUT_LINE(sysdate);
--计算明天的日期
DBMS_OUTPUT.PUT_LINE(pdate + 1);
end;
引用型变量和记录型变量
- 引用型变量
引用表中一列的类型作为我定义的变量类型
-- 引用型变量
set serveroutput on
declare
--定义引用变量:查询打印7839的姓名和薪水
--pename varchar2(20)
--psal number;
pename emp.ename%type;
psal emp.sal%type;
begin
--得到7839的姓名和薪水
select ename,sal into pename,psal from emp where empno = 7839;
--打印姓名和薪水
dbms_output.put_line(pename||'的薪水是'||psal);
end;
- 记录型变量
存储一行的数据类型的数组
set serveroutput on
--定义记录变量:查询打印7839的姓名和薪水
set serveroutput on
declare
--定义记录型变量:注意代表一行
emp_rec emp%rowtype;
begin
--得到7839一行的信息
select * into emp_rec from emp where empno = 7839;
--打印姓名和薪水
dbms_output.put_line(emp_rec.ename||'的薪水是'||emp_rec.sal);
end;
If语句的使用
/*
判断用户从键盘输入的数字
1.如何使用if语句
2.接收一个键盘输入(字符串)
*/
set serveroutput on
--接收一个键盘输入
--num:地址值,含义是:该地址上保存了输入的值
accept num prompt '请输入一个数字';
declare
--定义变量保存从键盘上输入的数字
pnum number := #
begin
--执行if语句进行条件判断
if pnum = 0 then dbms_output.put_line('您输入的数字是0');
elsif pnum = 1 then dbms_output.put_line('您输入的数字是1');
elsif pnum = 2 then dbms_output.put_line('您输入的数字是2');
else dbms_output.put_line('您输入的数字是其他');
end if;
end;
循环语句使用
- while
--使用while循环打印数字1—10
set serveroutput on
declare
--定义循环变量
pnum number := 1;
begin
while pnum <= 10 loop
--循环体
dbms_output.put_line(pnum);
--使该变量+1
pnum := pnum + 1;
end loop;
end;
- loop
--使用loop循环打印数字1—10
set serveroutput on
declare
--定义循环变量
pnum number := 1;
begin
loop
--退出条件:循环变量大于10
exit when pnum > 10;
--打印该变量的值
dbms_output.put_line(pnum);
--循环变量+1
pnum := pnum+1;
end loop;
end;
光标
- 光标就是一个结果集(result set)
- 例子1
--光标的使用例子:查询打印所有员工的薪水
set serveroutput on
/**
%found %notfound
*/
declare
--定义一个光标
cursor cemp is select ename,sal from emp;
--为光标定义对应的变量
pename emp.ename%type;
psal emp.sal%type;
begin
--打开光标
open cemp;
loop
--取一条记录
fetch cemp into pename,psal;
exit when cemp%notfound;
dbms_output.put_line(pename||'的薪水是'||psal);
end loop;
--关闭光标
close cemp;
end;
- 例子2
--光标的使用例子:给员工涨工资,总裁1000,经理800,其他400
set serveroutput on
/**
%found %notfound
*/
declare
--定义一个光标代表给哪些员工涨工资
cursor cemp is select empno,empjob from emp;
--为光标定义对应的变量
pempno emp.empno%type;
pjob emp.empjob%type;
begin
--打开光标
open cemp;
loop
fetch cemp into pempno,pjob;
exit when cemp%notfound;
--判断员工的职位
if pjob = 'president' then update emp set sal=sal+1000 where empno = pempno;
elsif pjob = 'manger' then update emp set sal=sal+800 where empno = pempno;
else update emp set sal=sal+400 where empno = pempno;
end if;
end loop;
--关闭光标
close cemp;
--对于oracle,默认的事务隔离级别是read committed
commit;
dbms_output.put_line('涨工资完成');
end;
- 例子3:带参数的光标定义
--查询某个部门中员工的姓名
set serveroutput on
declare
--定义带参数的光标
cursor cemp(dno number) is select ename from emp where deptno = dno;
pename emp.ename%type;
begin
--打开光标
open cemp(20);
loop
--取出每个员工的姓名
fetch cemp into pename;
exit when cemp%notfound;
dbms_output.put_line(pename);
end loop;
end;
光标属性
/**
光标的属性
%found %notfound
%isopen:判断光标是否打开
%rowcount:影响行数
*/
set serveroutput on
declare
--定义光标
cursor cemp is select empno,empjob from emp;
pempno emp.empno%type;
pjob emp.empjob%type;
begin
open cemp;
if cemp%isopen then
dbms_output.put_line('光标已经打开');
else
dbms_output.put_line('光标没有打开');
end if;
loop
--取出一条记录
fetch cemp into pempno,pjob;
exit when cemp%notfound;
--打印rowcount的值
dbms_output.put_line('rowcount:' || cemp%rowcount);
end loop;
close cemp;
end;
光标数限制
默认情况下oracle只允许在同一个回话中,打开300个光标。
- 在管理员用户下查询当前允许打开多少个光标
SQL>show parameter cursor
NAME | TYPE | VALUE |
---|---|---|
cursor_bind_capture_destination | string | memory+disk |
cursor_invalidation | string | IMMEDIATE |
cursor_sharing | string | EXACT |
cursor_space_for_time | boolean | FALSE |
open_cursors | integer | 300 |
session_cached_cursors | integer | 50 |
- 修改光标数的限制
SQL>alert system set open_cursors = 400 scope = both;
例外(异常)
-
例外是程序设计语言提供的一种功能,用来增强程序的健壮性和容错性。
-
系统例外
--系统例外:no_data_found
set serveroutput on
declare
pename emp.ename%type;
begin
--查询员工是1234的员工姓名
select ename into pename from emp where empno = 1234;
exception
when no_data_found then dbms_output.put_line('没有找到该员工');
when others then dbms_output.put_line('其他例外');
end;
--系统例外:too_many_rows
set serveroutput on
declare
pename emp.ename%type;
begin
--查询员工是1234的员工姓名
select ename into pename from emp where deptno = 20;
exception
when too_many_rows then dbms_output.put_line('select into 匹配多行');
when others then dbms_output.put_line('其他例外');
end;
--系统例外:zero_divide
set serveroutput on
declare
pnum number;
begin
pnum := 1/0;
exception
when zero_divide then dbms_output.put_line('0不能作为除数');
dbms_output.put_line('erro x/0');
when others then dbms_output.put_line('其他例外');
end;
--系统例外:value_error
set serveroutput on
declare
pnum number;
begin
pnum := 'abc';
exception
when value_error then dbms_output.put_line('算术或者转换异常');
when others then dbms_output.put_line('其他例外');
end;
- 自定义例外
--系统例外:value_error
set serveroutput on
declare
--定义光标,代表50号部门的员工姓名
cursor cemp is select ename from emp where deptno = 50;
pename emp.ename%type;
--自定义例外
no_emp_found exception;
begin
--打开光标
open cemp;
fetch cemp into pename;
if cemp%notfound then
--抛出例外
raise no_emp_found;
end if;
dbms_output.put_line('抛出例外了,这里不执行');
--oracle自动启动pmon(process monitor)
--关闭光标
close cempp;
exception
when value_error then dbms_output.put_line('算术或者转换异常');
when others then dbms_output.put_line('其他例外');
end;
案例
/*
需求统计2021年—2022年每年入职的员工人数
SQL语句
select to_char(hiredate,'yyyy') from emp;
->关标->循环->退出条件:notfound
变量:1.初始值 2.如何得到
每年入职的员工人数:
count21 number := 0;
count22 number := 0;
*/
set serverout on;
declare
cursor cemp is select to_char(hiredate,'yyyy') from emp;
phiredate varchar2(4);
count21 number := 0;
count22 number := 0;
begin
open cemp;
loop
fetch cemp into phiredate;
exit when cemp%notfound;
if phiredate = '2022' then count22 := count22+1;
elsif phiredate = '2021' then count21 := count21+1;
end if;
end loop;
dbms_output.put_line('2022年入职的人数有:'||count22);
dbms_output.put_line('2021年入职的人数有:'||count21);
close cemp;
end;
/*
需求:为员工涨工资。从最低工资涨起每人涨10%,但所有人的工资总额不能超过5w,
请计算涨工资的人数和涨工资后的工资总额,并输出涨工资的人数和工资总额。
*/
set serveroutput on
declare
cursor cemp is select empno,sal from emp order by sal asc;
pempno emp.empno%type;
psal emp.sal%type;
--涨工资的人数
countEmp number := 0;
--涨工资后的工资总额
salTotal number;
begin
--得到工资总额的初始值
select sum(sal) into salTotal from emp;
open cemp;
loop
--退出循环条件1、工资总额大于5w
exit when salTotal > 50000;
--取一个员工涨工资
fetch cemp into pempno,psal;
--退出循环条件2 %notfound
exit when cemp%notfound;
--涨后的工资总额=涨前的工资总额 + sal*0.1
salTotal := salTotal + psal*0.1;
if salTotal >= 50000 then
--把salTotal还原
salTotal := salTotal - psal*0.1;
elsif salTotal < 50000 then
--涨工资
update emp set sal = sal*1.1 where empno = pempno;
--人数 + 1
countEmp := countEmp + 1;
end if;
end loop;
close cemp;
commit;
dbms_output.put_line('涨工资人数:'||countEmp||' 涨后的工资总额:'||salTotal);
end;
/*
*实现按部门分段(6000以上、(6000、3000)、3000以下)统计各个工资的职工人数、以及
*各部门的工资总额(工资总额中不包括奖金)
*创建表msg存储统计数据
* create table msg(deptno number,count1 number,count2 number,count3 number,saltotal number)
*/
set serveroutput on
truncate table msg;
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;
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;
创建表存储统计结果
create table msg1(
coursename varchar2(20),
dname varchar2(20),
count1 number,
count2 number,
count3 number,
avggrade number
);
set serveroutput on
declare
--系的光标
cursor cdept is select dno,dname from dep;
pdno dep.dno%type;
pdname dep.dname%type;
--成绩光标
cursor cgrade(coursename varchar2,depno number)
is select grade from sc where cno=(select cno from course where cname=coursename)
and sno in (select sno from student where dno=depno);
pgrade sc.grade%type;
--每个分数段的人数
count1 number; count2 number; count3 number;
--每个系选修了"大学物理" 学生的平均成绩
avggrade number;
--课程名称
pcourseName varchar2(20):='大学物理';
begin
--打开系的光标
open cdept;
loop
--取一个系的信息
fetch cdept into pdno,pdname;
exit when cdept%notfound;
--初始化工作
count1:=0;count2:=0;count3:=0;
--系的平均成绩
select avg(grade) into avggrade from sc where cno=(select cno from course where cname=pcourseName)
and sno in (select sno from student where dno=pdno);
--取系中,选修了大学物理的学生成绩
open cgrade(pcourseName,pdno);
loop
--取出一个学生的成绩
fetch cgrade into pgrade;
exit when cgrade%notfound;
--判断成绩的范围
if pgrade<60 then count1:=count1+1;
elsif pgrade>=60 and pgrade<85 then count2:=count2+1;
else count3:=count3+1;
end if;
end loop;
close cgrade;
--保存当前的结构
insert into msg1 values(pcourseName,pdname,count1,count2,count3,avggrade);
end loop;
--关闭系光标
close cdept;
commit;
dbms_output.put_line('统计完成');
end;