调试nocopy功能时,为了看到实际效果,设置了一个比较大的循环次数,结果悲剧了:
运行了近1个小时没出结果,电脑死机(任务管理器都打不开);无奈只能强行关机,开机时间又特别长,一度让我以为系统崩溃。
看来,PL/SQL developer下调试这种暴力计算的程序风险很高啊,我在Eclipse下调试Java程序时从来没碰到过这种造成电脑死机的情况。
不过,这章的内容实用性很强,死机也值了(有点心疼电脑了)
-代码13.11 使用%Type定义形式参数 CREATE OR REPLACE PROCEDURE calcRaisedSalaryWithTYPE( p_job IN emp.job%TYPE, p_salary IN OUT emp.sal%TYPE --定义输入输出参数 ) AS v_sal NUMBER(10,2); --保存调整后的薪资值 BEGIN if p_job='职员' THEN --根据不同的job进行薪资的调整 v_sal:=p_salary*1.12; ELSIF p_job='销售人员' THEN v_sal:=p_salary*1.18; ELSIF p_job='经理' THEN v_sal:=p_salary*1.19; ELSE v_sal:=p_salary; END IF; p_salary:=v_sal; --将调整后的结果赋给输入输出参数 END calcRaisedSalaryWithTYPE; --过程调用受形参的约束,违反约束,将触发异常 DECLARE v_sal NUMBER(8,2); --薪资变量 v_job VARCHAR2(10); --职位变量 BEGIN v_sal:=1232945.45; v_job:='职员'; calcRaisedSalaryWithTYPE(v_job,v_sal); --计算调薪 DBMS_OUTPUT.put_line('计算后的调整薪水为:'||v_sal); --获取调薪后的结果 EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.put_line(SQLCODE||' '||SQLERRM); END; --参数传递有两种方式:按位置传递(类似Java的方法签名,实参与形参的类型一一对应)和按名称传递(如下例所示) DECLARE v_sal NUMBER(8,2); --薪资变量 v_job VARCHAR2(10); --职位变量 BEGIN v_sal:=123294.45; v_job:='职员'; calcRaisedSalaryWithTYPE(p_job=>v_job,p_salary=>v_sal); --计算调薪 DBMS_OUTPUT.put_line('计算后的调整薪水为:'||v_sal); --获取调薪后的结果 EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.put_line(SQLCODE||' '||SQLERRM); END; --按名称传递参数 DECLARE v_sal NUMBER(7,2); --薪资变量 v_job VARCHAR2(10); --职位变量 BEGIN v_sal:=124.45; v_job:='职员'; calcRaisedSalaryWithTYPE(p_salary=>v_sal,p_job=>v_job); --计算调薪 DBMS_OUTPUT.put_line('计算后的调整薪水为:'||v_sal); --获取调薪后的结果 EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.put_line(SQLCODE||' '||SQLERRM); END; --混合使用按名称传递和按位置传递 DECLARE v_sal NUMBER(7,2); --薪资变量 v_job VARCHAR2(10); --职位变量 BEGIN v_sal:=1224.45; v_job:='职员'; calcRaisedSalaryWithTYPE(p_salary=>v_sal,v_job); --计算调薪 DBMS_OUTPUT.put_line('计算后的调整薪水为:'||v_sal); --获取调薪后的结果 EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.put_line(SQLCODE||' '||SQLERRM); END; --代码13.12 指定形式参数的默认值 CREATE OR REPLACE PROCEDURE newdeptwithdefault ( p_deptno dept.deptno%TYPE DEFAULT 57, --部门编号 p_dname dept.dname%TYPE:='管理部', --部门名称 p_loc dept.loc%TYPE DEFAULT '江苏' --位置 ) AS v_deptcount NUMBER; --保存是否存在员工编号 BEGIN SELECT COUNT (*) INTO v_deptcount FROM dept WHERE deptno = p_deptno; --查询在dept表中是否存在部门编号 IF v_deptcount > 0 --如果存在相同的员工记录 THEN --抛出异常 raise_application_error (-20002, '出现了相同的部门记录'); END IF; INSERT INTO dept(deptno, dname, loc) VALUES (p_deptno, p_dname, p_loc);--插入记录 END; BEGIN newdeptwithdefault; --不指定任何参数,将使用形参默认值 END; BEGIN newdeptwithdefault(58,'事务组'); --不指定任何参数,将使用形参默认值 END; BEGIN newdeptwithdefault(58,'事务组'); END; --按名称传递参数,灵活性更高 BEGIN newdeptwithdefault(p_deptno=>59,p_loc=>'南海'); --让dname使用默认值 END; SELECT * FROM dept; --代码13.13 Nocopy使用示例 DECLARE TYPE emptabtyp IS TABLE OF emp%ROWTYPE; --定义嵌套表类型 emp_tab emptabtyp := emptabtyp (NULL); --定义一个空白的嵌套表变量 t1 NUMBER (5); --定义保存时间的临时变量 t2 NUMBER (5); t3 NUMBER (5); PROCEDURE get_time (t OUT NUMBER) --获取当前时间 IS BEGIN SELECT TO_CHAR (SYSDATE, 'SSSSS') --获取从午夜到当前的秒数 INTO t FROM DUAL; DBMS_OUTPUT.PUT_LINE(t); END; PROCEDURE do_nothing1 (tab IN OUT emptabtyp) --定义一个空白的过程,具有IN OUT参数 IS BEGIN NULL; END; PROCEDURE do_nothing2 (tab IN OUT NOCOPY emptabtyp) --在参数中使用NOCOPY编译提示 IS BEGIN NULL; END; BEGIN SELECT * INTO emp_tab (1) FROM emp WHERE empno = 5093; --查询emp表中的员工,插入到emp_tab第1个记录 emp_tab.EXTEND (90000, 1); --拷贝第1个元素N次 get_time (t1); --获取当前时间 do_nothing1 (emp_tab); --执行不带NOCOPY的过程 get_time (t2); --获取当前时间 do_nothing2 (emp_tab); --执行带NOCOPY的过程 get_time (t3); --获取当前时间 DBMS_OUTPUT.put_line ('调用所花费的时间(秒)'); DBMS_OUTPUT.put_line ('--------------------'); DBMS_OUTPUT.put_line ('不带NOCOPY的调用:' || TO_CHAR (t2 - t1)); DBMS_OUTPUT.put_line ('带NOCOPY的调用:' || TO_CHAR (t3 - t2)); END; / --代码13.14 定义可被SQL语句调用的子程序(不知道这样做有什么实际意义) CREATE OR REPLACE FUNCTION getempdept( p_empno emp.empno%TYPE ) RETURN VARCHAR2 --参数必须是Oracle数据库类型 AS v_dname dept.dname%TYPE; BEGIN SELECT b.dname INTO v_dname FROM emp a,dept b WHERE a.deptno=b.deptno AND a.empno=p_empno; RETURN v_dname; --查询数据表,获取部门名称 EXCEPTION WHEN NO_DATA_FOUND THEN RETURN NULL; --如果出现查询不到数据,返回NULL END; SELECT empno 员工编号,getempdept(empno) 部门名称 from emp; --代码13.15 使用嵌套子程序示例 CREATE OR REPLACE FUNCTION getraisedsalary_subprogram (p_empno emp.empno%TYPE) RETURN NUMBER IS v_salaryratio NUMBER (10, 2); --调薪比率 v_sal emp.sal%TYPE; --薪资变量 --定义内嵌子函数,返回薪资和调薪比率 FUNCTION getratio(p_sal OUT NUMBER) RETURN NUMBER IS n_job emp.job%TYPE; --职位变量 n_salaryratio NUMBER (10, 2); --调薪比率 BEGIN --获取员工表中的薪资信息 SELECT job, sal INTO n_job, p_sal FROM emp WHERE empno = p_empno; CASE n_job --根据不同的职位获取调薪比率 WHEN '职员' THEN n_salaryratio := 1.09; WHEN '销售人员' THEN n_salaryratio := 1.11; WHEN '经理' THEN n_salaryratio := 1.18; ELSE n_salaryratio := 1.1; END CASE; RETURN n_salaryratio; END; BEGIN v_salaryratio:=getratio(v_sal); --调用嵌套函数,获取调薪比率和员工薪资 IF v_salaryratio <> 1 --如果有调薪的可能 THEN RETURN ROUND(v_sal * v_salaryratio,2); --返回调薪后的薪资 ELSE RETURN v_sal; --否则不返回薪资 END IF; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN 0; --如果没找到原工记录,返回0 END; BEGIN --调用函数获取调薪后的记录 DBMS_OUTPUT.PUT_LINE('7369员工调薪记录:'||getraisedsalary_subprogram(7369)); DBMS_OUTPUT.PUT_LINE('7521员工调薪记录:'||getraisedsalary_subprogram(7521)); END; --代码13.17 使用前向声明进行互调用(这是在踢皮球啊,呵呵) DECLARE v_val BINARY_INTEGER:=5; PROCEDURE B(p_counter IN OUT BINARY_INTEGER); --前向声明嵌套子程序B PROCEDURE A(p_counter IN OUT BINARY_INTEGER) IS --声明嵌套子程序A BEGIN DBMS_OUTPUT.PUT_LINE('A('||p_counter||')'); IF p_counter>0 THEN B(p_counter); --在嵌套子程序中调用B p_counter:=p_counter-1; END IF; END A; PROCEDURE B(p_counter IN OUT BINARY_INTEGER) IS --声明嵌套子程序B BEGIN DBMS_OUTPUT.PUT_LINE('B('||p_counter||')'); p_counter:=p_counter-1; A(p_counter); --在嵌套子程序中调用A END B; BEGIN B(v_val); --调用嵌套子程序B END; --代码13.18 嵌套子程序重载示例 DECLARE PROCEDURE GetSalary(p_empno IN NUMBER) IS --带一个参数的过程 BEGIN DBMS_OUTPUT.put_line('员工编号为:'||p_empno); END; PROCEDURE GetSalary(p_empname IN VARCHAR2) IS --重载的过程 BEGIN DBMS_OUTPUT.put_line('员工名称为:'||p_empname); END; PROCEDURE GETSalary(p_empno IN NUMBER,p_empname IN VARCHAR) IS --生的过程 BEGIN DBMS_OUTPUT.put_line('员工编号为:'||p_empno||' 员工名称为:'||p_empname); END; BEGIN GetSalary(7369); --调用重载方未予 GetSalary('史密斯'); GetSalary(7369,'史密斯'); END; SELECT * FROM emp; CREATE TABLE emp_history AS SELECT * FROM emp WHERE 1=2; SELECT * FROM emp_history; --代码13.19 自治事务使用示例(提交自治事务,不影响主事务) DECLARE PROCEDURE TestAutonomous(p_empno NUMBER) AS PRAGMA AUTONOMOUS_TRANSACTION; --标记为自治事务 BEGIN --现在过程中是自治的事务,主事务被挂起 INSERT INTO emp_history SELECT * FROM emp WHERE empno=p_empno; COMMIT; --提交自治事务,不影响主事务 END TestAutonomous; BEGIN --主事务开始执行 INSERT INTO emp_history(empno,ename,sal) VALUES(1011,'测试',1000); TestAutonomous(7369); --主事务挂起,开始自治事务 ROLLBACK; --回滚主事务 END; select * from emp_history where ename='测试'; --代码13.20 在PL/SQL中实现递归阶乘(这个挺有意思!) DECLARE v_result INTEGER; FUNCTION fac(n POSITIVE) RETURN INTEGER IS --阶乘的返回结果 BEGIN IF n=1 THEN --如果n=1则终止条件 DBMS_OUTPUT.put('1!=1*0!'); RETURN 1; ELSE DBMS_OUTPUT.put(n||'!='||n||'*'); RETURN n*fac(n-1); --否则进行递归调用自身 END IF; END fac; BEGIN v_result:= fac(10); --调用阶乘函数 DBMS_OUTPUT.put_line('结果是:'||v_result); --输出阶乘结果 END; --代码13.21 使用递归查找职员列表示例(这个貌似挺有意思,可惜数据不够,调试时没看到效果) DECLARE PROCEDURE find_staff (mgr_no NUMBER, tier NUMBER := 1) IS boss_name VARCHAR2 (10); --定义老板的名称 CURSOR c1 (boss_no NUMBER) --定义游标来查询emp表中当前编号下的员工列表 IS SELECT empno, ename FROM emp WHERE mgr = boss_no; BEGIN SELECT ename INTO boss_name FROM emp WHERE empno = mgr_no; --获取管理者名称 IF tier = 1 --如果tier指定1,表示从最顶层开始查询 THEN INSERT INTO staff VALUES (boss_name || ' 是老板 '); --因为第1层是老板,下面的才是经理 END IF; FOR ee IN c1 (mgr_no) --通过游标FOR循环向staff表插入员工信息 LOOP INSERT INTO staff VALUES ( boss_name || ' 管理 ' || ee.ename || ' 在层次 ' || TO_CHAR (tier)); find_staff (ee.empno, tier + 1); --在游标中,递归调用下层的员工列表 END LOOP; COMMIT; END find_staff; BEGIN find_staff(7566); --查询7839的管理下的员工的列表和层次结构 END; CREATE TABLE staff(emplist VARCHAR2(1000)); SELECT * FROM staff; SELECT * FROM emp where empno; truncate table staff; --代码13.22 子程序依赖性示例 CREATE OR REPLACE PROCEDURE TestDependence AS BEGIN --向emp表插入测试数据 INSERT INTO emp(empno,ename,sal) VALUES(1011,'测试',1000); TestSubProg(7369); --ROLLBACK; END; --被另一个过程调用,用来向emp_history表插入数据 CREATE OR REPLACE PROCEDURE TestSubProg(p_empno NUMBER) AS BEGIN INSERT INTO emp_history SELECT * FROM emp WHERE empno=p_empno; END TestSubProg; select * from emp; select count(*) from emp_history; --查看依赖 SELECT name,type FROM user_dependencies WHERE referenced_name='EMP'; EXEC deptree_fill('TABLE','SCOTT','EMP'); --查看直接和间接依赖(需要先运行utldtgree.sql脚本) SELECT nested_level, NAME, TYPE FROM deptree WHERE TYPE IN ('PROCEDURE', 'FUNCTION'); --过程和函数所依赖的表更改后,会失效,需重新编译(原代码顺序不对,容易误导,现已调试成功) ALTER TABLE emp_history ADD emp_desc VARCHAR2(200) NULL; ALTER TABLE emp_history DROP COLUMN emp_desc; alter procedure testsubprog compile; alter procedure TESTDEPENDENCE compile; SELECT object_name, object_type, status FROM user_objects WHERE object_name in ('TESTDEPENDENCE','TESTSUBPROG'); --子程序权限管理(留待后续学习) CREATE USER userb IDENTIFIED BY userb; --创建用户userb,密码也为userb GRANT RESOURCE,CONNECT TO userb; --为userb分配角色 GRANT EXECUTE ON find_staff TO userb; --使得userb可以执行find_staff DROP USER userb; GRANT EXECUTE ON find_staff TO userb;
时间: 2024-10-25 07:36:14