1,游标的使用
2,异常错误处理
3,存储函数和过程
4,触发器
显式游标处理需四个 PL/SQL 步骤:
CURSOR cursor_name[(parameter[, parameter]…)] IS select_statement;
游标参数只能为输入参数,其格式为:
parameter_name [IN] datatype [{:= | DEFAULT} expression]
在指定数据类型时,不能使用长度约束。如 NUMBER(4)、CHAR(10) 等都是错误的。
OPEN cursor_name[([parameter =>] value[, [parameter =>] value]…)];
在向游标传递参数时,可以使用与函数参数相同的传值方法,即位置表示法和名称表示 法。PL/SQL
程序不能用 OPEN 语句重复打开一个游标。
FETCH cursor_name INTO {variable_list | record_variable };
对该记录进行处理;继续处理,直到活动集合中没有记录;
CLOSE cursor_name;
declare
---定义游标
cursor emp_cursor(emp_no number default 6) is
select ename, sal from emp where rownum < emp_no order by sal;
v_name emp.ename%type;
v_sal emp.sal%type;
begin
--打开游标
open emp_cursor(emp_no => 6);
--提取游标数据
fetch emp_cursor
into v_name, v_sal;
while emp_cursor%found loop
dbms_output.put_line(v_name || '---' || v_sal);
fetch emp_cursor
into v_name, v_sal;
end loop;
--关闭游标
close emp_cursor;
end;
%FOUND 布尔型属性,当最近一次读记录时成功返回,则值为 TRUE;
%NOTFOUND 布尔型属性,与%FOUND 相反;
%ISOPEN 布尔型属性,当游标已打开时返回 TRUE;
%ROWCOUNT 数字型属性,返回已从游标中读取的记录数。
例:给工资低于 3000 的员工工资加30:
declare
v_empno emp.empno%type;
v_sal emp.sal%type;
cursor emp_cursor is
select empno, sal from emp;
begin
open emp_cursor;
loop
fetch emp_cursor
into v_empno, v_sal;
exit when emp_cursor %notfound;
if v_sal <= 3000 then
update emp set sal = sal + 30
where empno = v_empno;
dbms_output.put_line('员工号' || v_empno || '工资已更新');
end if;
end loop;
dbms_output.put_line('记录数为' || emp_cursor %rowcount);
close emp_cursor;
end;
FOR index_variable IN cursor_name[value[, value]…] LOOP
-- 游标数据处理代码
END LOOP;
其中:
index_variable 为游标 FOR 循环语句隐含声明的索引变量,该变量为 记录变量,其 结构与游标查询语句返回的结构集合的结构相同。在程序中可以通过引用该索引记录变量元素来读取所提取的游标数据,index_variable 中各元素的名称与游标查询语句选择列表中所制定的列名相同。 如果在游标查询语句的选择列表中存在计算列,则必须为这些计算列指定别名后才能通过游标 FOR 循环语句中的索引变量来访问这些列数据
declare
cursor c_emp(dep_id number default 20) is
select empno, sal from emp where deptno = dep_id;
begin
for v_emp in c_emp(30) loop
dbms_output.put_line(v_emp.empno || ',' || v_emp.sal);
end loop;
end;
由系统隐含创建的游标称为隐式游标, 隐式游标的名字为 SQL,对于隐式游标的操作,如定义、打开、取值及关闭操作,都由 ORACLE 系统自动地完成,无需用户进行处理。 用户只能通过隐式游标的相关属性,来完成相应的操作。
隐式游标属性
SQL%FOUND 布尔型属性,当最近一次读记录时成功返回,则值为 TRUE;
SQL%NOTFOUND 布尔型属性,与%FOUND 相反;
SQL %ROWCOUNT 数字型属性, 返回已从游标中读取得记录数;
SQL %ISOPEN 布尔型属性, 取值总是 FALSE。SQL 命令执行完毕立即关闭隐式游标。
例题:更新指定员工信息,如果该员工没有找到,则打印”查无此人”信息:
declare
v_name emp.ename%type;
v_id emp.empno%type := &v_id;
begin
update emp set ename = v_name where empno = v_id;
if SQL%NOTFOUND then
dbms_output.put_line('查无此人');
end if;
end;
SELECT . . . FROM … FOR UPDATE [OF column[, column]…] [NOWAIT]
如果使用 FOR UPDATE 在 声明游标,则可在 DELETE 和 和 UPDATE 用 语句中使用 WHERE CURRENT OF
cursor_name 子句,修改或删除游标结果
例 :从 EMPLOYEES 表中查询某部门的员工情况,将其工资最低定为 1000:
declare
v_dep_id emp.deptno%type := &v_dep_id;
cursor emp_cursor is
select ename, sal from emp where deptno = v_dep_id for update nowait;
begin
for emp_rec in emp_cursor loop
if emp_rec.sal < 1000 then
update emp set sal = 1000 where current of emp_cursor;
end if;
end loop;
end;
①, 预定义 ( Predefined ) 错误
ORACLE 预定义的异常情况大约有 24 个。对这种异常情况的处理,无需在程序中定义,由ORACLE 自动将其引发。
②, 非预定义 ( Predefined )错误
即其他标准的 ORACLE 错误。对这种异常情况的处理,需要用户在程序中定义,然后由 ORACLE 自动将其引发。
③, 用户定义(User_define) 错误
程序执行过程中,出现编程人员认为的非正常情况。对这种异常情况的处理, 需要 用户在程序中定义,然后显式地在程序中将其引发。
EXCEPTION
WHEN first_exception THEN <code to handle first exception >
WHEN second_exception THEN <code to handle second exception >
WHEN OTHERS THEN <code to handle others exception >
END;
declare
v_empno emp.empno%type := &v_empno;
v_sal emp.sal%type;
begin
select sal into v_sal from emp where empno = v_empno for update;
if v_sal <= 1000 then
update emp set sal = sal + 30 where empno = v_empno;
dbms_output.put_line('编码为' || v_empno || '员工工资已更新');
else
dbms_output.put_line('编码为' || v_empno || '员工工资不需要更新');
end if;
exception
when NO_DATA_FOUND then
dbms_output.put_line('数据库中没有编码为' || v_empno || '的员工');
when TOO_MANY_ROWS then
dbms_output.put_line('程序运行错误,请使用游标');
when others then
dbms_output.put_line('其他错误');
end;
<异常情况> EXCEPTION;
PRAGMA EXCEPTION_INIT(< 异常情况>, < 错误代码>);
declare
v_depno emp.deptno%type := &v_depno;
deptno_remaining exception;
-- -2292是违反一致性约束的错误代码
pragma exception_init(deptno_remaining, -2292);
begin
delete from emp where deptno = v_depno;
exception
when deptno_remaining then
dbms_output.put_line('违反数据完整性约束');
when others then
dbms_output.put_line(sqlcode || '--' || sqlerrm);
end;
①, 在 PL/SQL 块的定义部分定义异常情况:
< 异常情况> EXCEPTION;
②, RAISE < 异常情况>;
③, 在 PL/SQL 块的异常情况处理部分对异常情况做出相应的处理。
declare
v_empid emp.empno%type := &v_empid;
no_result exception;
begin
update emp set sal = sal + 100 where empno = v_empid;
if sql%notfound then
raise no_result;
end if;
exception
when no_result then
dbms_output.put_line('数据更新失败');
when others then
dbms_output.put_line('其他异常');
end;
SQLCODE 返回错误代码数字
SQLERRM 返回错误信息
如: SQLCODE=-100 ------》 SQLERRM=’no_data_found ‘
SQLCODE=0 -----》 SQLERRM=’normal, successfual completion’
例 . 查询 ORACLE 错误代码:
BEGIN
INSERT INTO emp
(empno, ename, hiredate, deptno)
VALUES
(2222, 'Jerry', SYSDATE, 20);
DBMS_OUTPUT.PUT_LINE('插入数据记录成功!');
INSERT INTO emp
(empno, ename, hiredate, deptno)
VALUES
(2222, 'Jerry', SYSDATE, 20);
DBMS_OUTPUT.PUT_LINE('插入数据记录成功!');
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLCODE || '---' || SQLERRM);
END;
语法如下:
CREATE [OR REPLACE] FUNCTION function_name
[ (argment [ { IN | IN OUT }] Type,
argment [ { IN | OUT | IN OUT } ] Type ]
[ AUTHID DEFINER | CURRENT_USER ]
RETURN return_type
{ IS | AS }
<类型.变量的说明>
BEGIN
FUNCTION_body
EXCEPTION
其它语句
END;
create or replace function test_fun return date is
v_date date;
begin
select sysdate into v_date from dual;
dbms_output.put_line('我是函数哈^^');
return v_date;
end;
--执行函数
declare v_date date;
begin
v_date := test_fun(); dbms_output.put_line(v_date);
end;
create or replace function get_salary(dep_id emp.deptno%type,
emp_count out number) return number is
v_sum number;
begin
select sum(salary), count(1)
into v_sum, emp_count
from emp
where deptno = dep_id;
return v_sum;
exception
when no_data_found then
dbms_output.put_line('你需要的数据不存在');
when others then
dbms_output.put_line(sqlcode || ':' || sqlerrm);
end;
argument_value1[,argument_value2 …]
declare
v_num number;
v_sum number;
begin
v_sum := get_sal(80, v_num);
dbms_output.put_line('80号部门的工资总和:' || v_sum || ',人数' || v_num);
end;
argument => parameter [,…]
declare
v_num number;
v_sum number;
begin
v_sum := get_sal(emp_count => v_num,deptno => 80);
dbms_output.put_line('80号部门的工资总和:' || v_sum || ',人数' || v_num);
end;
declare
v_num number;
v_sum number;
begin
v_sum := get_sal(80, emp_count => v_num);
dbms_output.put_line('80号部门的工资总和:' || v_sum || ',人数' || v_num);
end;
论采用哪一种参数传递方法,实际参数和形式参数之间的数据传递只有两种方法 :传址法和传值法。
所谓传址法是指在调用函数时, 将实际参数的地址指针传递给形式参数, 使形式参数和实际参数指向内存中的同一区域,从而实现参数数据的传递。这种方法又称作参照法,即形式参数参照实际参数数据。 输入参数均采用传址法传递数据。
传值法是指将实际参数的数据拷贝到形式参数,而不是传递实际参数的地址。默认时, 输出参数和输入/ 输出参数均采用传值法。
删除指定员工记录:
create or replace procedure del_emp(v_empid in emp.empno%type) is
no_result exception;
begin
delete from emp where empno = v_empid;
if sql%notfound then
raise no_result;
end if;
dbms_output.put_line('编号为' || v_empid || '的员工已被除名');
exception
when no_result then
dbms_output.put_line('你要删除的数据不存在');
when others then
dbms_output.put_line(sqlcode || '--' || sqlerrm);
end;
EXEC[UTE] Procedure_name( parameter1, parameter2…);
create or replace procedure query_map(v_empid emp.empno%type,
v_name out emp.ename%type,
v_sal out emp.sal%type) is
begin
select ename, sal into v_name, v_sal from emp where empno = v_empid;
dbms_output.put_line('员工号为:' || v_empid || '的员工已经找到');
exception
when no_data_found then
dbms_output.put_line('你要查询的数据不存在');
when others then
dbms_output.put_line(sqlcode || '--' || sqlerrm);
end;
declare
v1 emp.ename%type;
v2 emp.sal%type;
begin
query_map(7499, v1, v2);
dbms_output.put_line('姓名' || v1 || ',工资' || v2);
query_map(201, v1, v2);
dbms_output.put_line('姓名' || v1 || ',工资' || v2);
end;
create or replace procedure proc_demo(v_depid emp.deptno%type default 10,
v_salsum out emp.sal%type,
v_empcount out number) is
begin
select sum(sal), count(1)
into v_salsum, v_empcount
from emp
where deptno = v_depid;
exception
when no_data_found then
dbms_output.put_line('你要查询的数据不存在');
when others then
dbms_output.put_line(sqlcode || '--' || sqlerrm);
end;
declare
v_num number;
v_sum number;
begin
proc_demo(v_salsum => v_sum, v_empcount => v_num);
dbms_output.put_line('10号部门的工资总额为:' || v_sum || ',人数为:' || v_num);
end;
DROP PROCEDURE [user.]Procudure_name;
触发器组成:
CREATE [OR REPLACE] TRIGGER trigger_name
{BEFORE | AFTER }
{INSERT | DELETE | UPDATE [OF column [, column …]]}
ON [schema.] table_name
[FOR EACH ROW ]
[WHEN condition]
trigger_body;
触发器名可以和表或过程有相同的名字,但在一个模式中触发器名不能相同。
触发器的限制
CREATE TRIGGER 语句文本的字符长度不能超过 32KB;
触发器体内的 SELECT 语句只能为 SELECT … INTO … 结构,或者为定义游标所使用的 SELECT 语句。
触发器中不能使用数据库事务控制语句 COMMIT; ROLLBACK, SVAEPOINT 语句;
由触发器所调用的过程或函数也不能使用数据库事务控制语句;
建立一个触发器,当职工表emp表被删除一条记录时,把被删除记录写到职工表删除日志表中去
create table emp_his as select * from emp where 1=2
create or replace trigger del_emp_trigger
before delete on emp
for each row
begin
insert into emp_his
(deptno, empno, ename, job, mgr, sal, comm, hiredate)
values
(:old.deptno,
:old.empno,
:old.ename,
:old.job,
:old.mgr,
:old.sal,
:old.comm,
:old.hiredate);
end;
DROP TRIGGER trigger_name;
当删除其他用户模式中的触发器名称,需要具有 DROP ANY TRIGGER 系统权限,当删除建立在数据库上的触发器时,用户需要具有 ADMINISTER DATABASE TRIGGER 系统权限。此外,, 当删除表或视图时,建立在这些对象上的触发器也随之删除。