您的位置:首页 > 数据库

PL/SQL

2015-12-12 21:36 381 查看
/*
什么是PL/SQL?
(1)PL/SQL 是过程语言(Procedural Language)与结构化查询语言(SQL)结合而成的编程语言。
(2)PL/SQL 是对 SQL 的扩展。
(3)支持多种数据类型,如大对象和集合类型,可使用条件和循环等控制结构。
(4)可用于创建存储过程、函数、触发器和程序包,给SQL语句的执行添加程序逻辑。

优点:
(1)支持 SQL,在 PL/SQL 中可以使用:
a) 数据操纵命令:DML,DDL
b) 事务控制命令:TCL
c) 游标控制
d) SQL函数和SQL运算符

(2)与 SQL 紧密集成,简化数据处理。
a) 支持所有 SQL 数据类型
b) 支持 NULL 值
c) 支持 %TYPE(引用字段类型:employee.ename%TYPE)和 %ROWTYPE(引用表的行类型:employee%TYPE)属性类型

(3)支持面向对象编程 (OOP)
(4)可移植性,可运行在任何操作系统和平台上的Oralce 数据库
(5)更佳的性能,PL/SQL 经过编译执行

PL/SQL基本单元分为三个部分:1)声明部分declare(可选)。 2)可执行部分。3)异常处理部分(可选)。
PL/SQL语法:
[DECLARE
声明变量、常量、游标、自定义记录类型等。
]
BEGIN
执行部分。

[EXCEPTION
异常处理部分。
]
END;
中括号括起的部分为可选部分。

变量与常量的声明和使用:
(1) 在声明部分声明,使用前必须先声明。
(2) 声明时必须指定数据类型,每行声明一个标识符。
(3) 在可执行部分的 SQL 语句和过程语句中使用。
语法:
identifier [CONSTANT] datatype [NOT NULL] [:= value];
或。
identifier [CONSTANT] datatype [NOT NULL] [DEFAULT expr];

给变量赋值有两种方式:
(1) 使用赋值运算符" := "
(2) 使用: SELECT  column_list INTO  variable_list FROM table ....语句

如果PL/SQL拿到命令行去执行看不到输出的话,需要用以下命令打oracle服务端输出,
否则PL/SQL、过程等的输出是看不到的:
set serveroutput on;
*/

--1. 最简单的PL/SQL块
declare
--没有声明变量等
begin
dbms_output.put('hello '); --将内容放入到输出缓存中,不会立即输出内容
dbms_output.put_line('PL/SQL!');--立即输出内容
end;
/

--变量的声明和使用
declare
--identifier [CONSTANT] datatype [NOT NULL] [:= value];
v_ename varchar2(20) := 'james zhang';
v_ename2 varchar2(20) default 'zhang san';
v_empno int;
begin
-- "&"号在PL/SQL中表示替代变量,可用它来在运行时接收输入值,如果输入的值字符串则用单引号括起来
v_ename := '&请输入姓名:';
v_empno := &请输入编号;
dbms_output.put_line(v_ename);
dbms_output.put_line(v_ename2);
dbms_output.put_line(v_empno);
end;
/

--使用类型引用方式来声明变量
declare
v_empno employee.empno%TYPE;
v_ename employee.ename%type;
v_sal employee.sal%type;
--声明一个能存储employee行类型的变量
rec_emp employee%rowtype;

--接收输入的值
v_in_empno employee.empno%TYPE;

begin
v_in_empno := &请输入员工编号;

/*通过查询记录来给变量赋值
SELECT  column_list INTO  variable_list FROM table ....语句, 只能返回一行,如果多行会报异常
*/
select empno,ename,sal into v_empno,v_ename,v_sal
from employee where empno=v_in_empno;
--输出
dbms_output.put_line('编号:'||v_empno||',姓名:'||v_ename||',工资:'||v_sal);

--查询记录为行类型变量赋值
select * into rec_emp from employee where empno=v_in_empno;
dbms_output.put_line('==>>编号:'||rec_emp.empno||',姓名:'||rec_emp.ename||',工资:'||rec_emp.sal);
end;
/

/*
(二) PL/SQL 支持的流程控制结构:
a) 条件控制
i.    IF、IF-ELSE 语句
ii.   CASE 语句
b) 循环控制
i.    LOOP 循环
ii.   WHILE 循环
iii.  FOR 循环
(三) 顺序控制
a) GOTO 语句
b) NULL
(四) NULL 语句
(五) 异常处理
a)Oracle预定义异常
b)自定义异常
*/

/*
a) 条件控制: if, if-else
i.  IF 、IF-ELSE 语句
语法:
IF <condition> THEN
statement....
[ELSE]
statement....
END IF;
每一个if都必须对应的有一个end if;
*/

--根据某员工薪资情况对薪资做出调整:?<3000的加500,?>3000的加250,
declare
v_empno employee.empno%type;
v_sal employee.sal%type;

begin
v_empno := &请输入员工编号:;
--如果查询无数据时给变量赋值会有异常
select sal into v_sal from employee where empno=v_empno;
dbms_output.put_line('编号:'||v_empno||'的员工工资是:'||v_sal);

if v_sal<=3000.0 then
update employee set sal=sal+500 where empno=v_empno;
else
update employee set sal=sal+250 where empno=v_empno;
end if;
commit; --提交事务

select sal into v_sal from employee where empno=v_empno;
dbms_output.put_line('更新后,编号:'||v_empno||'的员工工资是:'||v_sal);

--异常处理
exception
when NO_DATA_FOUND then
dbms_output.put_line('员工:'||v_empno||'不存在。');
when others then --相当于java的Exception
dbms_output.put_line('更新员工:'||v_empno||'失败。');
end;
/

--多重IF-ELSE 判断学生成绩,显示:80>=优秀,70<=良好<80,60<=及格<70,60>不及格
declare
v_score integer;

begin
v_score := &请输入学生成绩:;
if v_score>=80 then
dbms_output.put_line('优秀:'||v_score);
else if v_score>=70 then
dbms_output.put_line('良好:'||v_score);
else if v_score>=60 then

101de
dbms_output.put_line('及格:'||v_score);
else
dbms_output.put_line('不及格:'||v_score);
end if;
end if;
end if;
end;
/

/*
a) 条件控制: CASE
ii. CASE 语句
*/
--判断学生成绩
--case 第1种用法:可用来匹配数值范围
declare
v_score integer;

begin
v_score := &请输入学生成绩:;
case
when v_score>=80  then
dbms_output.put_line('优秀:'||v_score);
when v_score>=70  then
dbms_output.put_line('良好:'||v_score);
when v_score>=60  then
dbms_output.put_line('及格:'||v_score);
else
dbms_output.put_line('不及格:'||v_score);
end case;
end;
/

--case第2种 用法:只能精确匹配值(匹配一个常量值)
declare
v_score char(1);
begin
v_score := upper('&请输入学生成绩:');

case v_score
when 'A'  then
dbms_output.put_line('优秀:'||v_score);
when 'B'  then
dbms_output.put_line('良好:'||v_score);
when 'C'  then
dbms_output.put_line('及格:'||v_score);
else
dbms_output.put_line('不及格:'||v_score);
end case;
end;
/

/*
b)   循环控制:LOOP, WHILE, FOR
i.    LOOP 无条件循环
语法:
LOOP
statement...;
IF 条件 THEN
EXIT;
END IF;
或用[EXIT WHEN 条件;]
END LOOP;

在循环体内可通过:EXIT,或 EXIT WHEN 条件  来退出循环。
*/
--打印输出格式:1,2,3.....10
declare
i int := 1;
begin
loop
dbms_output.put(i); --不换行, dbms_output.put_line();--会换行输出
if i<=9 then
dbms_output.put(',');
end if;
exit when i=10;
i := i+1;
end loop;
dbms_output.new_line; --输出缓存的内容
end;
/

/*
打印直角三角形(一行一列,二行二列....)
*
**
***
****
*****
******
*******

*/
declare
v_row int := 1;
v_col int := 0;
v_line int;
begin
v_line := &请输入行数:;
loop
v_col := 0;
loop
if v_col<v_row then
dbms_output.put('*');
end if;
exit when v_col=v_row;
v_col := v_col+1;
end loop;
dbms_output.new_line;
exit when v_row=v_line; --10行
v_row := v_row + 1;
end loop;
end;
/

/*
b)   WHILE 循环控制
i.  LOOP 无条件循环
ii. WHILE 循环: 有条件循环。
WHILE语法:
WHILE 条件 LOOP
statement;
[EXIT WHEN 条件;]
END LOOP;

在循环体内也可通过:EXIT, EXIT WHEN 条件  来使循环退出。
*
***
*****
*******
4-line
print 2*line-1
*/
declare
v_row int := 1;
v_col int := 0;
v_blank int := 0;
begin
while v_row<=4 loop
v_blank := 0;
--输出空格
while v_blank<(4-v_row) loop
dbms_output.put(' ');
v_blank := v_blank+1;
end loop;

v_col := 0;
--输出*号
while v_col<(2*v_row-1) loop
dbms_output.put('*');
v_col := v_col+1;
end loop;

dbms_output.new_line;--换行
v_row := v_row+1;
end loop;
end;
/

/*
b)  FOR 循环控制
i.    LOOP 循环
ii.   WHILE 循环
iii.  FOR 循环:有次数的循环。
FOR语法:
FOR 计数器变量(oracle自动创建无需预先声明) IN [REVERSE] min_value..max_value LOOP

END LOOP;
如:
FOR i in [reverse] 1..10 LOOP
--statement;
END LOOP;
*/
begin
--for循环的不变量不需要事先声明,oracle会自动创建
for i in reverse 1..10 loop
dbms_output.put(i||',');
end loop;
dbms_output.new_line;
end;
/

--
begin
--for循环的不变量不需要事先声明,oracle会自动创建
for e in (select * from employee order by empno) loop
dbms_output.put_line(e.empno||','||e.ename||','||e.job||','||e.sal);
end loop;
dbms_output.new_line;
end;
/

/*
53:根据输入部门编号,按以下格式打印各部门人员姓名:
部门名称:RESEARCH
部门人员:SMITH,JONES,FORD

提示:灵活利用子查询和rownum伪列
1. 确定循环的次数:count(*) cnt
2. rownum=(1...cnt)
*/
declare
v_rownum int;
v_ename string(20);
v_dname string(20);
v_deptno department.deptno%type;
begin
v_deptno := &请输入部门编号:;

select dname,count(e.ename) into v_dname,v_rownum
from employee e, department d
where e.deptno=d.deptno
and d.deptno=v_deptno
group by dname;

dbms_output.put_line('部门名称:'||v_dname);
dbms_output.put('员工名称:');

for i in 1..v_rownum loop
select ename into v_ename
from (
select ename,rownum rn from emp where deptno=v_deptno
) a
where a.rn=i;
dbms_output.put(v_ename);
if i<v_rownum then
dbms_output.put(',');
end if;
end loop;

dbms_output.new_line;
end;
/

--方式2
declare
v_rownum int := 1;
v_ename string(20);
v_dname string(20);
v_deptno department.deptno%type;
begin
v_deptno := &请输入部门编号:;

select dname into v_dname
from department
where  deptno=v_deptno;

--统计行数
select count(ename) into v_rownum from emp where deptno=v_deptno;

dbms_output.put_line('部门名称:'||v_dname);
dbms_output.put('员工名称:');

--输入本部门中的员工名字
for e in (select ename,rownum rn from emp where deptno=v_deptno) loop
dbms_output.put(e.rn||'.'||e.ename);

if e.rn<v_rownum then
dbms_output.put(',');
end if;
end loop;

dbms_output.new_line;
end;
/

/*
(三) 顺序控制结构
a) GOTO 语句,跳转到后面的语句去执行,只能向后跳不能向前跳。
*/
--若员工的工资小于资金的3倍,则为员工的工资增加30%。
declare
v_empno emp.empno%type;
v_ename emp.ename%type;
v_sal emp.sal%type;
v_comm emp.comm%type;
begin
v_empno := &请输入员工编号:;

select ename,sal,comm into v_ename,v_sal,v_comm
from emp
where empno=v_empno;

dbms_output.put_line(v_ename||' 的工资是:'||v_sal||', 奖金是:'||v_comm);

if v_sal<(v_comm*3) then
goto updating; --跳转到<<updating>>
else
goto quit;
end if;

<<updating>>
update emp set sal=sal+sal*0.3 where empno=v_empno;
commit;
dbms_output.put_line(v_ename||'工资修改成功!工资修改后为:'||(v_sal+v_sal*0.3));
<<quit>>
null; --空语句
end;
/

/*
(五) 异常处理
a)Oracle预定义异常

oracle常见预定义异常:

错误号     异常错误信息名称                          说明
----------------------------------------------------------------------
ORA-0001    DUP_VAL_ON_INDEX                       试图破坏一个唯一性限制
ORA-0051    TIMEOUT_ON_RESOURCE(少用)        在等待资源时发生超时
ORA-0061    TRANSACTION_BACKED_OUT(少用)   由于发生死锁事务被撤消
ORA-1001    INVALID_CURSOR                         试图使用一个未打开的游标
ORA-1012    NOT_LOGGED_ON(少用)                    没有连接到ORACLE
ORA-1017    LOGIN_DENIED(少用)                     无效的用户名/口令
ORA-1403    NO_DATA_FOUND                            SELECT INTO 没有找到数据
ORA-1422    TOO_MANY_ROWS                            SELECT INTO 返回多行
ORA-1476    ZERO_DIVIDE                              试图被零除
ORA-1722    INVALID_NUMBER                       转换一个数字失败
VALUE_ERROR                            值错误
INVALID_NUMBER                       无效的数字
OTHERS                                   其它的异常(总是放在异常处理块的最后,就象java中Exception的处理放在最后一样)

Oracle内置函数 SQLCODE 和 SQLERRM 是特别用在OTHERS异常处理器中,分别用来返回Oracle的错误代码和错误消息。
OTHERS处理器应该是异常处理块中的最后的异常处理器,因为它是用来捕获除了别的异常处理器处理以外的所有的
Oracle异常,所以在程序的最外层使用一个OTHERS处理器的话,将可以确保所有的错误都会被检测到。如果没有异常被触发,
则SQLCODE返回 0。

Oracle内置函数 SQLCODE 和 SQLERRM 是特别用在OTHERS异常处理器中,分别用来返回Oracle的错误代码和错误消息。

*/
-- a)Oracle预定义异常
declare
rec_emp emp%rowtype;
begin
-- select * into rec_emp from emp where empno=333;
select * into rec_emp from emp;

--异常处理
exception
when NO_DATA_FOUND then
dbms_output.put_line('员工不存!错误码:'||SQLCODE||',错误信息:'||SQLERRM);

when TOO_MANY_ROWS then
dbms_output.put_line('查询结果员工数过多!错误码:'||SQLCODE||',错误信息:'||SQLERRM);

when others then
dbms_output.put_line('未知异常!错误码:'||SQLCODE||',错误信息:'||SQLERRM);
end;
/

/*
(五) 异常处理
异常处理的步骤:
1. 声明异常
2. 抛出异常:raise ex
3. 处理异常:exception when ex then .....

a)Oracle预定义异常
b)自定义异常
*/
--用户自定义异常
declare
--1. 声明自定义的异常
ex_salGreatThan exception;
rec_emp emp%rowtype;
v_comm emp.comm%type;
begin
select * into rec_emp from emp where empno=8002;
v_comm := &请输入员工的奖金:;

if v_comm<=100 or v_comm>=500 then
--2.抛出异常
raise ex_salGreatThan;
else
--更新奖金
null;
end if;

--3. 处理异常:exception when ex then .....
exception
when ex_salGreatThan then
dbms_output.put_line('更改的奖金过高,奖金必须在: ¥100~¥500之间!错误码:'||SQLCODE||',错误信息:'||SQLERRM);

when others then
dbms_output.put_line('未知异常!错误码:'||SQLCODE||',错误信息:'||SQLERRM);

end;
/

/*

RAISE_APPLICATION_ERROR 存储过程:
(1) 用于创建用户定义的错误信息
(2) 可以在可执行部分和异常处理部分使用
(3) 错误编号必须介于 -20000 和 -20999 之间
(4) 错误消息的长度可长达 2048 个字节
语法:
RAISE_APPLICATION_ERROR(error_number, error_message);

RAISE_APPLICATION_ERROR是专门有来将数据库程序的错误从数据库服务器端传到客户端应用程序如java程序。
*/

declare
--1. 声明自定义的异常
ex_salGreatThan exception;
rec_emp emp%rowtype;
v_comm emp.comm%type;
begin
select * into rec_emp from emp where empno=8002;
v_comm := &请输入员工的奖金:;

if v_comm<=100 or v_comm>=500 then
--2.抛出异常
raise ex_salGreatThan;
else
--更新奖金
null;
end if;

--3. 处理异常:exception when ex then .....
exception
when ex_salGreatThan then
raise_application_error(-20000,'更改的奖金过高,奖金必须在: ¥100~¥500之间!错误码:'||SQLCODE||',错误信息:'||SQLERRM);

when others then
raise_application_error(-20999,'未知异常!错误码:'||SQLCODE||',错误信息:'||SQLERRM);
end;
/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: