当前位置:Gxlcms > 数据库问题 > 源码-PL/SQL从入门到精通-第十三章-子程序-Part 2

源码-PL/SQL从入门到精通-第十三章-子程序-Part 2

时间:2021-07-01 10:21:17 帮助过:2人阅读

调试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;

源码-PL/SQL从入门到精通-第十三章-子程序-Part 2

标签:

人气教程排行