/* * PL/SQL 程序设计学习笔记 * 学习plSql介绍.pdf * 时间:2010-10-05 */ --创建DEPT表 create table DEPT ( DEPTNO NUMBER(10), DNAME NVARCHAR2(255), LOC NVARCHAR2(255) ) delete dept; select * from dept; --插入一条记录并显示 DECLARE Row_id UROWID; info VARCHAR2(40); BEGIN INSERT INTO dept VALUES (90, 'SERVICE', 'BEIJING') RETURNING rowid, dname||':'||to_char(deptno)||':'||loc INTO row_id, info; DBMS_OUTPUT.PUT_LINE('ROWID:'||row_id); DBMS_OUTPUT.PUT_LINE(info); END; --修改一条记录并显示 DECLARE Row_id UROWID; info VARCHAR2(40); BEGIN UPDATE dept SET deptno=80 WHERE DNAME='SERVICE' RETURNING rowid, dname||':'||to_char(deptno)||':'||loc INTO row_id, info; DBMS_OUTPUT.PUT_LINE('ROWID:'||row_id); DBMS_OUTPUT.PUT_LINE(info); END; --删除一条记录并显示 DECLARE Row_id UROWID; info VARCHAR2(40); BEGIN DELETE dept WHERE DNAME='SERVICE' RETURNING rowid, dname||':'||to_char(deptno)||':'||loc INTO row_id, info; DBMS_OUTPUT.PUT_LINE('ROWID:'||row_id); DBMS_OUTPUT.PUT_LINE(info); END; --复合类型-记录类型 DECLARE TYPE test_rec IS RECORD( Code VARCHAR2(10), Name VARCHAR2(30) NOT NULL :='a book'); V_book test_rec; BEGIN V_book.code :='123'; V_book.name :='C++ Programming'; DBMS_OUTPUT.PUT_LINE(v_book.code||v_book.name); END; --复合类型-使用%TYPE --创建emp表 create table emp ( empno NUMBER(10), ename NVARCHAR2(255), sal NUMBER(10,2), hiredate Timestamp, comm NUMBER(10,2), job NVARCHAR2(255), deptno NUMBER(10) ) insert into emp(empno,ename,sal,hiredate,comm,job,deptno) values(100,'Test',30.5,sysdate,10,'president',1); insert into emp(empno,ename,sal,hiredate,comm,job,deptno) values(7788,'Test2',100.23,sysdate,10.2,'manager',2); --drop table emp; select * from emp; DECLARE --用%TYPE 类型定义与表相配的字段 TYPE t_Record IS RECORD( T_no emp.empno%TYPE, T_name emp.ename%TYPE, T_sal emp.sal%TYPE ); --声明接收数据的变量 v_emp t_Record; BEGIN SELECT empno, ename, sal INTO v_emp FROM emp WHERE empno=7788; DBMS_OUTPUT.PUT_LINE (TO_CHAR(v_emp.t_no)||v_emp.t_name||TO_CHAR(v_emp.t_sal)); END; DECLARE v_empno emp.empno%TYPE :=&empno; Type r_record is record ( v_name emp.ename%TYPE, v_sal emp.sal%TYPE, v_date emp.hiredate%TYPE); Rec r_record; BEGIN SELECT ename, sal, hiredate INTO Rec FROM emp WHERE empno=v_empno; DBMS_OUTPUT.PUT_LINE(Rec.v_name||'--'||Rec.v_sal||'--'||Rec.v_date); END; --使用%ROWTYPE DECLARE v_empno emp.empno%TYPE :=&empno; rec emp%ROWTYPE; BEGIN SELECT * INTO rec FROM emp WHERE empno=v_empno; DBMS_OUTPUT.PUT_LINE('姓名:'||rec.ename||'工资:'||rec.sal||'工作时间:'||rec.hiredate); END; --以下脚本只能在sqlplus命令行中执行 ----start----- VARIABLE result2 NUMBER BEGIN SELECT (sal*12)+nvl(comm, 0) INTO :result2 FROM emp WHERE empno=7788; END; PRINT result2; -----end----- --INDEX BY TABLES select * from dept; insert into dept(deptno,dname,loc) values(1,'Test1','BEIJING'); insert into dept(deptno,dname,loc) values(10,'Test10','SHENGZHEN'); insert into dept(deptno,dname,loc) values(20,'Test20','CHANGSHA'); insert into dept(deptno,dname,loc) values(30,'Test30','ZHUHAI'); insert into dept(deptno,dname,loc) values(40,'Test40','SHAOYANG'); DECLARE TYPE dept_table_type IS TABLE OF dept%ROWTYPE INDEX BY BINARY_INTEGER; my_dname_table dept_table_type; v_count number(2) :=4; BEGIN FOR i IN 1 .. v_count LOOP SELECT * INTO my_dname_table(i) FROM dept WHERE deptno=i*10; END LOOP; FOR i IN my_dname_table.FIRST.. my_dname_table.LAST LOOP DBMS_OUTPUT.PUT_LINE('Department number: '||my_dname_table(i).deptno); DBMS_OUTPUT.PUT_LINE('Department name: '|| my_dname_table(i).dname); END LOOP; END; --BOOLEAN 赋值 DECLARE done BOOLEAN; /* the following statements are legal: */ BEGIN done := FALSE; WHILE NOT done LOOP Null; END LOOP; END; SELECT * FROM EMP --数据库赋值 DECLARE emp_id emp.empno%TYPE :=7788; emp_name emp.ename%TYPE; wages emp.sal%TYPE; BEGIN SELECT ename, NVL(sal,0) + NVL(comm,0) INTO emp_name, wages FROM emp WHERE empno = emp_id; Dbms_output.put_line(emp_name||'----'||to_char(wages)); END; SELECT * FROM emp; insert into emp(empno,ename,sal,hiredate,comm,job,deptno) values(8,'Test3',10.23,sysdate,1.2,'manager',3); insert into emp(empno,ename,sal,hiredate,comm,job,deptno) values(12,'Test4',10.23,sysdate,1.2,'president',4); delete emp where empno = 8; delete emp where empno = 12; --变量作用范围及可见性 DECLARE Emess char(80); BEGIN DECLARE V1 NUMBER(4); BEGIN SELECT empno INTO v1 FROM emp WHERE LOWER(job)='president'; DBMS_OUTPUT.PUT_LINE(V1); EXCEPTION When TOO_MANY_ROWS THEN DBMS_OUTPUT.PUT_LINE ('More than one president'); END; DECLARE V1 NUMBER(4); BEGIN SELECT empno INTO v1 FROM emp WHERE LOWER(job)='manager'; DBMS_OUTPUT.PUT_LINE(V1); EXCEPTION When TOO_MANY_ROWS THEN DBMS_OUTPUT.PUT_LINE ('More than one manager'); END; EXCEPTION When others THEN Emess:=substr(SQLERRM,1,80); DBMS_OUTPUT.PUT_LINE (emess); END; select * from emp; /* 本例子仅是一个简单的插入,不是实际应用。*/ DECLARE v_ename VARCHAR2(20) := 'Bill'; v_sal NUMBER(7,2) :=1234.56; v_deptno NUMBER(2) := 10; v_empno NUMBER(4) := 8888; BEGIN INSERT INTO emp ( empno, ename, JOB, sal, deptno , hiredate ) VALUES ( v_empno, v_ename, 'Manager', v_sal, v_deptno,TO_DATE('1954.06.09','yyyy.mm.dd') ); COMMIT; END; /* 本例子仅是一个简单的删除例子,不是实际应用。*/ DECLARE v_empno number(4) := 8888; BEGIN DELETE FROM emp WHERE empno=v_empno; COMMIT; END; select * from emp; --条件语句 DECLARE v_empno emp.empno%TYPE :=&empno; V_salary emp.sal%TYPE; V_comment VARCHAR2(35); BEGIN SELECT sal INTO v_salary FROM emp WHERE empno=v_empno; IF v_salary<1500 THEN V_comment:= 'Fairly less'; ELSIF v_salary <3000 THEN V_comment:= 'A little more'; ELSE V_comment:= 'Lots of salary'; END IF; DBMS_OUTPUT.PUT_LINE(V_comment); END; --CASE 表达式 DECLARE V_grade char(1) := UPPER('&p_grade'); V_appraisal VARCHAR2(20); BEGIN V_appraisal := CASE v_grade WHEN 'A' THEN 'Excellent' WHEN 'B' THEN 'Very Good' WHEN 'C' THEN 'Good' ELSE 'No such grade' END; DBMS_OUTPUT.PUT_LINE('Grade:'||v_grade||' Appraisal: '|| v_appraisal); END; --简单循环 DECLARE int NUMBER(2) :=0; BEGIN LOOP int := int + 1; DBMS_OUTPUT.PUT_LINE('int 的当前值为:'||int); EXIT WHEN int =10; END LOOP; END; --WHILE 循环 DECLARE x NUMBER; BEGIN x:= 1; WHILE x<10 LOOP DBMS_OUTPUT.PUT_LINE('X 的当前值为:'||x); x:= x+1; END LOOP; END; --数字式循环 BEGIN FOR int in 1..10 LOOP DBMS_OUTPUT.PUT_LINE('int 的当前值为: '||int); END LOOP; END; --数字式循环-循环变量自动减1 BEGIN FOR int in REVERSE 1..10 LOOP DBMS_OUTPUT.PUT_LINE('int 的当前值为: '||int); END LOOP; END; CREATE TABLE temp_table(num_col NUMBER); DECLARE V_counter NUMBER := 10; BEGIN INSERT INTO temp_table(num_col)VALUES (v_counter ); FOR v_counter IN 20 .. 25 LOOP INSERT INTO temp_table (num_col ) VALUES ( v_counter ); END LOOP; INSERT INTO temp_table(num_col)VALUES (v_counter ); FOR v_counter IN REVERSE 20 .. 25 LOOP INSERT INTO temp_table (num_col ) VALUES ( v_counter ); END LOOP; END; DROP TABLE temp_table; select * from temp_table; --标号和GOTO DECLARE V_counter NUMBER := 1; BEGIN LOOP DBMS_OUTPUT.PUT_LINE('V_counter 的当前值为:'||V_counter); V_counter := v_counter + 1; IF v_counter > 10 THEN GOTO l_ENDofLOOP; END IF; END LOOP; <<l_ENDofLOOP>> DBMS_OUTPUT.PUT_LINE('V_counter 的当前值为:'||V_counter); END; select * from dept; --游标参数的传递方法 DECLARE DeptRec dept%ROWTYPE; Dept_name dept.dname%TYPE; Dept_loc dept.loc%TYPE; CURSOR c1 IS SELECT dname, loc FROM dept WHERE deptno <= 30; CURSOR c2(dept_no NUMBER DEFAULT 10) IS SELECT dname, loc FROM dept WHERE deptno <= dept_no; CURSOR c3(dept_no NUMBER DEFAULT 10) IS SELECT * FROM dept WHERE deptno <=dept_no; BEGIN OPEN c1; LOOP FETCH c1 INTO dept_name, dept_loc; EXIT WHEN c1%NOTFOUND; DBMS_OUTPUT.PUT_LINE(dept_name||'---'||dept_loc); END LOOP; CLOSE c1; DBMS_OUTPUT.PUT_LINE('cl closed!'); OPEN c2; LOOP FETCH c2 INTO dept_name, dept_loc; EXIT WHEN c2%NOTFOUND; DBMS_OUTPUT.PUT_LINE(dept_name||'---'||dept_loc); END LOOP; CLOSE c2; DBMS_OUTPUT.PUT_LINE('c2 closed!'); OPEN c3(dept_no =>20); LOOP FETCH c3 INTO deptrec; EXIT WHEN c3%NOTFOUND; DBMS_OUTPUT.PUT_LINE(deptrec.deptno||'---'||deptrec.dname||'---'||deptrec.loc); END LOOP; CLOSE c3; DBMS_OUTPUT.PUT_LINE('c3 closed!'); END; SELECT * FROM dept WHERE deptno <=30; select * from emp; --给工资低于1200的员工增加工资50 DECLARE v_empno emp.empno%TYPE; v_sal emp.sal%TYPE; CURSOR c IS SELECT empno, sal FROM emp; BEGIN OPEN c; LOOP FETCH c INTO v_empno, v_sal; EXIT WHEN C%NOTFOUND; IF v_sal<=1200 THEN UPDATE emp SET sal=sal+50 WHERE empno=v_empno; DBMS_OUTPUT.PUT_LINE('编码为'||v_empno||'工资已更新!'); END IF; DBMS_OUTPUT.PUT_LINE('记录数:'||C%ROWCOUNT); END LOOP; CLOSE c; END; --游标的FOR循环 DECLARE CURSOR c_sal IS SELECT empno, ename, sal FROM emp; BEGIN --隐含打开游标 FOR v_sal IN c_sal LOOP --隐含执行一个FETCH 语句 DBMS_OUTPUT.PUT_LINE( to_char(v_sal.empno)||'---'||v_sal.ename||'---'||to_char(v_sal.sal)); --隐含监测c_sal%NOTFOUND END LOOP; --隐含关闭游标 END; --当所声明的游标带有参数时,通过游标FOR 循环语句为游标传递参数 DECLARE CURSOR c1(dept_no NUMBER DEFAULT 10) IS SELECT dname, loc FROM dept WHERE deptno <= dept_no; BEGIN DBMS_OUTPUT.PUT_LINE('dept_no 参数值为30:'); FOR c1_rec IN c1(30) LOOP DBMS_OUTPUT.PUT_LINE(c1_rec.dname||'---'||c1_rec.loc); END LOOP; DBMS_OUTPUT.PUT_LINE(CHR(10)||'使用默认的dept_no 参数值10:'); FOR c1_rec IN c1 LOOP DBMS_OUTPUT.PUT_LINE(c1_rec.dname||'---'||c1_rec.loc); END LOOP; END; --PL/SQL 还允许在游标FOR 循环语句中使用子查询来实现游标的功能。 BEGIN FOR c1_rec IN (SELECT dname, loc FROM dept) LOOP DBMS_OUTPUT.PUT_LINE(c1_rec.dname||'---'||c1_rec.loc); END LOOP; END; --删除EMP 表中某部门的所有员工,如果该部门中已没有员工,则在DEPT 表中删除该部门 DECLARE V_deptno emp.deptno%TYPE :=&p_deptno; BEGIN DELETE FROM emp WHERE deptno=v_deptno; IF SQL%NOTFOUND THEN DELETE FROM dept WHERE deptno=v_deptno; END IF; END; select * from emp; select * from dept; --从EMP表中查询某部门的员工情况,将其工资最低定为1500 DECLARE V_deptno emp.deptno%TYPE :=&p_deptno; CURSOR emp_cursor IS SELECT empno, sal FROM emp WHERE deptno=v_deptno FOR UPDATE OF sal NOWAIT; BEGIN FOR emp_record IN emp_cursor LOOP IF emp_record.sal< 1500 THEN UPDATE emp SET sal=1500 WHERE CURRENT OF emp_cursor; END IF; END LOOP; --COMMIT; END; select * from emp; --预定义的异常处理-更新指定员工工资,如工资小于1500,则加100 DECLARE v_empno emp.empno%TYPE :=&empno; v_sal emp.sal%TYPE; BEGIN SELECT sal INTO v_sal FROM emp WHERE empno=v_empno; IF v_sal<=1400 THEN UPDATE emp SET sal=sal+100 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; select * from dept; --删除指定部门的记录信息,以确保该部门没有员工。 INSERT INTO dept VALUES(50, 'FINANCE', 'CHICAGO'); DECLARE v_deptno dept.deptno%TYPE :=&deptno; e_deptno_remaining EXCEPTION; PRAGMA EXCEPTION_INIT(e_deptno_remaining,2292); /* 2292 是违反一致性约束的错误代码*/ BEGIN DELETE FROM dept WHERE deptno=v_deptno; EXCEPTION WHEN e_deptno_remainingTHEN DBMS_OUTPUT.PUT_LINE('违反数据完整性约束!'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('发生其它错误!'); END; select * from emp; --更新指定员工工资,增加100 DECLARE v_empno emp.empno%TYPE :=&empno; no_result EXCEPTION; BEGIN UPDATE emp SET sal=sal+100 WHERE empno=v_empno; 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; --创建一个函数get_salary, 该函数检索指定部门的工资总和,其中定义了20991和20992号错误,分别处理参数为空和非法部门代码两种错误 CREATE TABLE errlog(Errcode NUMBER,Errtext CHAR(40)); CREATE OR REPLACE FUNCTION get_salary(p_deptno NUMBER) RETURN NUMBER AS V_sal NUMBER; BEGIN IF p_deptno IS NULL THEN RAISE_APPLICATION_ERROR(-20991,'部门代码为空'); ELSIF p_deptno<0 THEN RAISE_APPLICATION_ERROR(-20992,'无效的部门代码'); ELSE SELECT SUM(sal) INTO v_sal FROM EMP WHERE deptno=p_deptno; RETURN V_sal; END IF; END; select * from emp; select * from errlog; delete errlog; DECLARE V_salary NUMBER(7,2); V_sqlcode NUMBER; V_sqlerr VARCHAR2(512); Null_deptno EXCEPTION; Invalid_deptno EXCEPTION; PRAGMA EXCEPTION_INIT(null_deptno,-20991); PRAGMA EXCEPTION_INIT(invalid_deptno, -20992); BEGIN V_salary :=get_salary(1); DBMS_OUTPUT.PUT_LINE('1 号部门工资:'||TO_CHAR(V_salary)); BEGIN V_salary :=get_salary(-10); EXCEPTION WHEN invalid_deptno THEN V_sqlcode :=SQLCODE; V_sqlerr :=SQLERRM; INSERT INTO errlog(errcode,errtext) VALUES(v_sqlcode, v_sqlerr); COMMIT; END inner1; V_salary :=get_salary(20); DBMS_OUTPUT.PUT_LINE('20 号部门工资:'||TO_CHAR(V_salary)); BEGIN V_salary :=get_salary(NULL); END inner2; V_salary :=get_salary(30); DBMS_OUTPUT.PUT_LINE('30 号部门工资:'||TO_CHAR(V_salary)); EXCEPTION WHEN null_deptno THEN V_sqlcode :=SQLCODE; V_sqlerr :=SQLERRM; INSERT INTO errlog(errcode,errtext) VALUES(v_sqlcode, v_sqlerr); COMMIT; WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('发生其它错误!'); END outer; --将ORACLE 错误代码及其信息存入错误代码表 CREATE TABLE errors(errnum NUMBER(4), errmsg VARCHAR2(100)); select * from errors; DECLARE err_msg VARCHAR2(100); BEGIN /* 得到所有ORACLE 错误信息*/ FOR err_num IN -100.. 0 LOOP err_msg := SQLERRM(err_num); INSERT INTO errors VALUES(err_num, err_msg); END LOOP; END; DROP TABLE errors; --查询ORACLE 错误代码 select * from emp; 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 get_salaryf( Dept_no NUMBER, Emp_count OUT NUMBER) RETURN NUMBER IS V_sum NUMBER; BEGIN SELECT SUM(sal), count(*) INTO V_sum, emp_count FROM emp WHERE deptno=dept_no; RETURN v_sum; EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('你需要的数据不存在!'); WHEN TOO_MANY_ROWS THEN DBMS_OUTPUT.PUT_LINE('程序运行错误!请使用游标'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('发生其它错误!'); END get_salaryf; select * from emp; --内嵌函数的调用-第一种参数传递格式称为位置表示法 --计算某部门的工资总和 DECLARE V_num NUMBER; V_sum NUMBER; BEGIN V_sum := get_salaryf(3, v_num); DBMS_OUTPUT.PUT_LINE('3 号部门工资总和:'||v_sum||',人数:'||v_num); END; --内嵌函数的调用-第二种参数传递格式称为名称表示法 DECLARE V_num NUMBER; V_sum NUMBER; BEGIN V_sum :=get_salaryf(emp_count=> v_num, dept_no => 3); DBMS_OUTPUT.PUT_LINE('3 号部门工资总和:'||v_sum||',人数:'||v_num); END; --内嵌函数的调用-第三种参数传递格式称为混合表示法 CREATE OR REPLACE FUNCTION demo_fun( Name VARCHAR2, Age INTEGER, Sex VARCHAR2) RETURN VARCHAR2 AS V_var VARCHAR2(32); BEGIN V_var := name||':'||TO_CHAR(age)||'岁,'||sex; RETURN v_var; END; DECLARE Var VARCHAR(32); BEGIN Var := demo_fun('user1', 30, sex => '男'); DBMS_OUTPUT.PUT_LINE(var); Var := demo_fun('user2', age => 40, sex => '男'); DBMS_OUTPUT.PUT_LINE(var); Var := demo_fun('user3', sex => '女', age => 20); DBMS_OUTPUT.PUT_LINE(var); END; --参数默认值 CREATE OR REPLACE FUNCTION demo_fun2( Name VARCHAR2, Age INTEGER, Sex VARCHAR2 DEFAULT '男') RETURN VARCHAR2 AS V_var VARCHAR2(32); BEGIN V_var := name||':'||TO_CHAR(age)||'岁,'||sex; RETURN v_var; END; DECLARE Var VARCHAR(32); BEGIN Var := demo_fun2('user1', 30); DBMS_OUTPUT.PUT_LINE(var); Var := demo_fun2('user2', age => 40); DBMS_OUTPUT.PUT_LINE(var); Var := demo_fun2('user3', sex => '女', age => 20); DBMS_OUTPUT.PUT_LINE(var); END; --存储过程 --用户连接登记记录 CREATE table logtable (userid VARCHAR2(10), logdate date); CREATE OR REPLACE PROCEDURE logexecution IS BEGIN INSERT INTO logtable (userid, logdate) VALUES (USER, SYSDATE); END; --删除指定员工记录 CREATE OR REPLACE PROCEDURE DelEmp(v_empno IN emp.empno%TYPE)AS No_result EXCEPTION; BEGIN DELETE FROM emp WHERE empno=v_empno; IF SQL%NOTFOUND THEN RAISE no_result; END IF; DBMS_OUTPUT.PUT_LINE('编码为'||v_empno||'的员工已被除名!'); EXCEPTION WHEN no_result THEN DBMS_OUTPUT.PUT_LINE('你需要的数据不存在!'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('发生其它错误!'); END DelEmp; --计算指定部门的工资总和,并统计其中的职工数量 CREATE OR REPLACE PROCEDURE proc_demo( Dept_no NUMBER DEFAULT 10, Sal_sum OUT NUMBER, Emp_count OUT NUMBER) IS BEGIN SELECT SUM(sal), COUNT(*) INTO sal_sum, emp_count FROM emp WHERE deptno=dept_no; EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('你需要的数据不存在!'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('发生其它错误!'); END proc_demo; select * from emp; DECLARE V_num NUMBER; V_sum NUMBER(8, 2); BEGIN Proc_demo(1, v_sum, v_num); DBMS_OUTPUT.PUT_LINE('1 号部门工资总和:'||v_sum||',人数:'||v_num); Proc_demo(sal_sum=> v_sum, emp_count => v_num); DBMS_OUTPUT.PUT_LINE('10 号部门工资总和:'||v_sum||',人数:'||v_num); END; --建立本地过程,用于计算指定部门的工资总和,并统计其中的职工数量 DECLARE V_num NUMBER; V_sum NUMBER(8, 2); PROCEDURE proc_demo( Dept_no NUMBER DEFAULT 10, Sal_sum OUT NUMBER, Emp_count OUT NUMBER) IS BEGIN SELECT SUM(sal), COUNT(*) INTO sal_sum, emp_count FROM emp WHERE deptno=dept_no; EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('你需要的数据不存在!'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('发生其它错误!'); END proc_demo; BEGIN Proc_demo(1, v_sum, v_num); DBMS_OUTPUT.PUT_LINE('1 号部门工资总和:'||v_sum||',人数:'||v_num); Proc_demo(sal_sum=> v_sum, emp_count => v_num); DBMS_OUTPUT.PUT_LINE('10 号部门工资总和:'||v_sum||',人数:'||v_num); END; --授权执行权给相关的用户或角色 CREATE OR REPLACE PUBLIC SYNONYM DBMS_JOB FOR DBMS_JOB; GRANT EXECUTE ON dbms_job TO PUBLIC WITH GRANT OPTION; --与过程相关数据字典 select * from USER_SOURCE; select * from ALL_SOURCE; select * from DBA_SOURCE; select * from USER_ERRORS; DESC proc_demo; desc emp; DROP PROCEDURE logexecution; select * from dept; --包的创建和应用 --包定义的说明 --创建的包为demo_pack, 该包中包含一个记录变量DeptRec、两个函数和一个过程 CREATE OR REPLACE PACKAGE demo_pack IS DeptRec dept%ROWTYPE; V_sqlcode NUMBER; V_sqlerr VARCHAR2(2048); FUNCTION add_dept( dept_no NUMBER, dept_name VARCHAR2, location VARCHAR2) RETURN NUMBER; FUNCTION remove_dept(dept_no NUMBER) RETURN NUMBER; PROCEDURE query_dept(dept_no IN NUMBER); END demo_pack; --包主体的创建 CREATE OR REPLACE PACKAGE BODY demo_pack IS Flag INTEGER; FUNCTION check_dept(dept_no NUMBER) RETURN INTEGER; FUNCTION add_dept(dept_no NUMBER, dept_name VARCHAR2, location VARCHAR2) RETURN NUMBER IS BEGIN IF check_dept(dept_no)=0 THEN INSERT INTO dept VALUES(dept_no, dept_name, location); RETURN 1; ELSE RETURN 0; END IF; EXCEPTION WHEN OTHERS THEN V_sqlcode := SQLCODE; V_sqlerr := SQLERRM; RETURN 1; END add_dept; FUNCTION remove_dept(dept_no NUMBER) RETURN NUMBER IS BEGIN V_sqlcode := 0; V_sqlerr := NULL; IF check_dept(dept_no)= 1 THEN DELETE FROM dept WHERE deptno=dept_no; RETURN 1; ELSE RETURN 0; END IF; EXCEPTION WHEN OTHERS THEN V_sqlcode := SQLCODE; V_sqlerr := SQLERRM; RETURN 1; END remove_dept; PROCEDURE query_dept(dept_no IN NUMBER) IS BEGIN IF check_dept(dept_no)=1 THEN SELECT * INTO DeptRec FROM dept WHERE deptno=dept_no; END IF; END query_dept; FUNCTION check_dept(dept_no NUMBER) RETURN INTEGER IS BEGIN SELECT COUNT(*) INTO flag FROM dept WHERE deptno=dept_no; IF flag > 0 THEN Flag := 1; END IF; RETURN flag; END check_dept; BEGIN V_sqlcode := NULL; V_sqlerr := NULL; END demo_pack; select * from dept; delete dept where deptno=90; --对包内共有元素的调用格式为:包名.元素名称 DECLARE Var NUMBER; BEGIN Var := demo_pack.add_dept(90,'Administration', 'Beijing'); IF Var =-1 THEN DBMS_OUTPUT.PUT_LINE(demo_pack.v_sqlerr); ELSIF Var =0 THEN DBMS_OUTPUT.PUT_LINE('该部门记录已经存在!'); ELSE DBMS_OUTPUT.PUT_LINE('添加记录成功!'); Demo_pack.query_dept(90); DBMS_OUTPUT.PUT_LINE(demo_pack.DeptRec.deptno||'---'||demo_pack.DeptRec.dname||'---'||demo_pack.DeptRec.loc); Var := demo_pack.remove_dept(90); IF Var =-1 THEN DBMS_OUTPUT.PUT_LINE(demo_pack.v_sqlerr); ELSE DBMS_OUTPUT.PUT_LINE('删除记录成功!'); END IF; END IF; END; select * from emp; --创建包emp_package CREATE OR REPLACE PACKAGE emp_package IS TYPE emp_table_type IS TABLE OF emp%ROWTYPE INDEX BY BINARY_INTEGER; PROCEDURE read_emp_table(p_emp_table OUT emp_table_type); END emp_package; CREATE OR REPLACE PACKAGE BODY emp_package IS PROCEDURE read_emp_table(p_emp_table OUT emp_table_type) IS I BINARY_INTEGER := 0; BEGIN FOR emp_record IN ( SELECT * FROM emp ) LOOP P_emp_table(i) := emp_record; I := I + 1; END LOOP; END read_emp_table; END emp_package; DECLARE E_table emp_package.emp_table_type; BEGIN Emp_package.read_emp_table(e_table); FOR I IN e_table.FIRST .. e_table.LAST LOOP DBMS_OUTPUT.PUT_LINE(e_table(i).empno||'---'||e_table(i).ename); END LOOP; END; --子程序重载 --PL/SQL 允许对包内子程序和本地子程序进行重载。所谓重载时指两个或多个子程序有相同 --的名称,但拥有不同的参数变量、参数顺序或参数数据类型。 CREATE OR REPLACE PACKAGE demo_pack1 IS DeptRec dept%ROWTYPE; V_sqlcode NUMBER; V_sqlerr VARCHAR2(2048); FUNCTION query_dept(dept_no IN NUMBER) RETURN INTEGER; FUNCTION query_dept(dept_no IN VARCHAR2) RETURN INTEGER; END demo_pack1; CREATE OR REPLACE PACKAGE BODY demo_pack1 IS FUNCTION check_dept(dept_no NUMBER) RETURN INTEGER IS Flag INTEGER; BEGIN SELECT COUNT(*) INTO flag FROM dept WHERE deptno=dept_no; IF flag > 0 THEN RETURN 1; ELSE RETURN 0; END IF; END check_dept; FUNCTION check_dept(dept_no VARCHAR2) RETURN INTEGER IS Flag INTEGER; BEGIN SELECT COUNT(*) INTO flag FROM dept WHERE deptno=dept_no; IF flag > 0 THEN RETURN 1; ELSE RETURN 0; END IF; END check_dept; FUNCTION query_dept(dept_no IN NUMBER) RETURN INTEGER IS BEGIN IF check_dept(dept_no)=1 THEN SELECT * INTO DeptRec FROM dept WHERE deptno=dept_no; RETURN 1; ELSE RETURN 0; END IF; END query_dept; FUNCTION query_dept(dept_no IN VARCHAR2) RETURN INTEGER IS BEGIN IF check_dept(dept_no)=1 THEN SELECT * INTO DeptRec FROM dept WHERE deptno=dept_no; RETURN 1; ELSE RETURN 0; END IF; END query_dept; END demo_pack1; --删除包 DROP PACKAGE demo_pack1; --包的管理 select * from DBA_SOURCE; select * from USER_SOURCE; select * from USER_ERRORS; select * from DBA_OBJECTS; select * from emp; select * from emp_his; insert into emp(empno,ename,sal,hiredate,comm,job,deptno) values(100,'Test',30.5,sysdate,10,'president',1); insert into emp(empno,ename,sal,hiredate,comm,job,deptno) values(7788,'Test2',100.23,sysdate,10.2,'manager',2); CREATE TABLE emp_his AS SELECT * FROM EMP WHERE 1=2; CREATE OR REPLACE TRIGGER del_emp BEFORE DELETE ON HAIYA1.emp FOR EACH ROW BEGIN --将修改前数据插入到日志记录表del_emp ,以供监督使用。 INSERT INTO emp_his(deptno, empno, ename, job, sal, comm, hiredate) VALUES( :old.deptno, :old.empno, :old.ename , :old.job, :old.sal, :old.comm, :old.hiredate ); END; --删除记录触发触发器 DELETE emp WHERE empno=7788; DROP TABLE emp_his; DROP TRIGGER del_emp; --创建替代(Instead_of)触发器 --我们可以创建INSTEAD_OF 触发器来为DELETE 操作执行所需的处理,即删除EMP表中所有基准行 CREATE OR REPLACE VIEW emp_view AS SELECT deptno, count(*) total_employeer, sum(sal) total_salary FROM emp GROUP BY deptno; select * from emp_view; CREATE OR REPLACE TRIGGER emp_view_delete INSTEAD OF DELETE ON emp_view FOR EACH ROW BEGIN DELETE FROM emp WHERE deptno= :old.deptno; END emp_view_delete; DELETE FROM emp_view WHERE deptno=1; DROP TRIGGER emp_view_delete; DROP VIEW emp_view; --改变一个触发器的状态 ALTER TRIGGER del_emp DISABLE; ALTER TRIGGER del_emp ENABLE; --使表EMP上的所有TRIGGER失效/生效 ALTER TABLE emp DISABLE ALL TRIGGERS; ALTER TABLE emp ENABLE ALL TRIGGERS; --触发器和数据字典 SELECT TRIGGER_NAME, TRIGGER_TYPE, TRIGGERING_EVENT,TABLE_OWNER, BASE_OBJECT_TYPE, REFERENCING_NAMES,STATUS, ACTION_TYPE FROM user_triggers; --数据库触发器的应用举例 BEGIN --创建用于记录事件日志的数据表 DBMS_UTILITY.EXEC_DDL_STATEMENT(' CREATE TABLE eventlog( Eventname VARCHAR2(20) NOT NULL, Eventdate date default sysdate, Inst_num NUMBER NULL, Db_name VARCHAR2(50) NULL, Srv_error NUMBER NULL, Username VARCHAR2(30) NULL, Obj_type VARCHAR2(20) NULL, Obj_name VARCHAR2(30) NULL, Obj_owner VARCHAR2(30) NULL ) '); --创建DDL 触发器trig4_ddl DBMS_UTILITY.EXEC_DDL_STATEMENT(' CREATE OR REPLACE TRIGGER trig_ddl AFTER CREATE OR ALTER OR DROP ON DATABASE DECLARE Event VARCHAR2(20); Typ VARCHAR2(20); Name VARCHAR2(30); Owner VARCHAR2(30); BEGIN --读取DDL 事件属性 Event := SYSEVENT; Typ := DICTIONARY_OBJ_TYPE; Name := DICTIONARY_OBJ_NAME; Owner := DICTIONARY_OBJ_OWNER; --将事件属性插入到事件日志表中 INSERT INTO HAIYA1.eventlog(eventname,obj_type, obj_name, obj_owner) VALUES(event, typ, name, owner); END; '); --创建LOGON、STARTUP 和SERVERERROR 事件触发器 DBMS_UTILITY.EXEC_DDL_STATEMENT(' CREATE OR REPLACE TRIGGER trig_after AFTER LOGON OR STARTUP OR SERVERERROR ON DATABASE DECLARE Event VARCHAR2(20); Instance NUMBER; Err_num NUMBER; Dbname VARCHAR2(50); User VARCHAR2(30); BEGIN Event := SYSEVENT; IF event = ''LOGON'' THEN User := LOGIN_USER; INSERT INTO eventlog(eventname,username) VALUES(event, user); ELSIF event = ''SERVERERROR'' THEN Err_num := SERVER_ERROR(1); INSERT INTO eventlog(eventname,srv_error) VALUES(event, err_num); ELSE Instance := INSTANCE_NUM; Dbname := DATABASE_NAME; INSERT INTO eventlog(eventname,inst_num, db_name) VALUES(event, instance, dbname); END IF; END; '); --创建LOGOFF 和SHUTDOWN 事件触发器 DBMS_UTILITY.EXEC_DDL_STATEMENT(' CREATE OR REPLACE TRIGGER trig_before BEFORE LOGOFF OR SHUTDOWN ON DATABASE DECLARE Event VARCHAR2(20); Instance NUMBER; Dbname VARCHAR2(50); User VARCHAR2(30); BEGIN Event := SYSEVENT; IF event = ''LOGOFF'' THEN User := LOGIN_USER; INSERT INTO eventlog(eventname,username) VALUES(event, user); ELSE Instance := INSTANCE_NUM; Dbname := DATABASE_NAME; INSERT INTO eventlog(eventname,inst_num, db_name) VALUES(event, instance, dbname); END IF; END; '); END; CREATE TABLE mydata(mydate NUMBER); drop table mydata; select * from mydata; SELECT eventname, eventdate, obj_type, obj_name, obj_owner, username, Srv_error FROM eventlog; select * from eventlog; drop table eventlog; delete eventlog; --删除触发器 DROP TRIGGER trig_ddl; DROP TRIGGER trig_before; DROP TRIGGER trig_after; --删除记录事件日志的数据表 DROP TABLE eventlog;