源码-PL/SQL从入门到精通-第十三章-子程序-Part 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;
时间: 2024-10-25 07:36:14

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

源码-PL/SQL从入门到精通-第四章-PL/SQL控制语句(基础中的基础)

控制语句对任何一门语言.任何一个程序员而言,都是基础中的基础. 学了PL/SQL,才体会到Java语法的简洁,简直不是一个时代的东西:当然,他们的定位本来就不一样,不能以己之长比之他人之短. --第4章开始 --代码4.1 最简单的IF语句使用示例 DECLARE v_count NUMBER(10) := 0; --定义计数器变量 v_empno NUMBER(4) := 7888; --定义员工编号 BEGIN SELECT COUNT(1) --首先查询指定的员工编号是否存在 INTO v

源码-PL/SQL从入门到精通-第七章-操作数据表

除了序列(Sequence)和同义词(Synonym)外,其他语句基本上都在自己创建的iTerm数据中用过了,所以这章感觉不难. --第7章开始 --7.1.2 插入单行记录 SELECT * FROM emp; INSERT INTO emp (empno, ename, job, mgr, hiredate, sal, comm, deptno ) VALUES (7890, '刘七', '副理', 7566, TO_DATE ('2001-08-15', 'YYYY-MM-DD'), 80

源码-PL/SQL从入门到精通-第二章-PL/SQL基本概念-Part 1

随书附带的源码没有序号,部分有bug,调试过程中一并更正. --代码2.1 使用书序结构计算员工薪资 DECLARE v_sal1 NUMBER; v_sal2 NUMBER; v_sumsal NUMBER; BEGIN SELECT sal INTO v_sal1 FROM emp WHERE empno = &empno1; SELECT sal INTO v_sal2 FROM emp WHERE empno = &empno2; v_sumsal := v_sal1 + v_sa

源码-PL/SQL从入门到精通-第十章-使用游标-Part 2

虽说下个项目中要用到PL/SQL,但还不清楚需要用到哪些具体的知识点?估计游标是不可或缺了,如果能在现有代码的基础上改一改就能满足项目要求,现在的工作也就更有价值了. --代码10.14 基本的Loop循环结构 DECLARE dept_row dept%ROWTYPE; --定义游标结果记录变量 CURSOR dept_cursor IS SELECT * FROM dept; --定义游标变量 BEGIN OPEN dept_cursor; --打开游标 LOOP --简单循环 FETCH

D3.js的v5版本入门教程(第十三章)—— 饼状图

D3.js的v5版本入门教程(第十三章) 这一章我们来绘制一个简单的饼状图,我们只绘制构成饼状图基本的元素——扇形.文字,从这一章开始,内容可能有点难理解,因为每一章都会引入比较多的难理解知识点,在这里作者本人也只是粗略的讲解每个新知识点的意思!如果不是很理解的话,需要读者自行查看官网API 为了绘制一个饼状图,我们还是需要以下新的知识点 d3.arc( {} ),弧形生成器,用以绘制弧形,需要传入一些用以绘制弧形基本的数据的对象,例如,该对象的属性可以包括(我用官网api的示例) d3.pie

MyBatis源码分析-SQL语句执行的完整流程

MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以对配置和原生Map使用简单的 XML 或注解,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录.如何新建MyBatis源码工程请点击MyBatis源码分析-IDEA新建MyBatis源码工程. MyBatis框架主要完成的是以下2件事情: 根据JD

Oracle PL/SQL语言入门

简介:PL/SQL(Procedural Language/Structured Query Language)是一种过程化语言,属于第三代语言,它与C.C++.Java等语言一样关注于处理细节,可以用来实现比较复杂的业务逻辑.它允许SQL的数据操纵语言和查询语句包含在块结构(block_structured)和代码过程语言中,使PL/SQL成为一个功能强大的事务处理语言. 一.背景介绍 结构化查询语言(Structured Query Language,简称SQL)是用来访问关系型数据库一种通

数据库分库分表中间件 Sharding-JDBC 源码分析 —— SQL 解析(六)之删除SQL

关注微信公众号:[芋道源码]有福利: RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表 RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址 您对于源码的疑问每条留言都将得到认真回复.甚至不知道如何读源码也可以请教噢. 新的源码解析文章实时收到通知.每周更新一篇左右. 认真的源码交流微信群. 本文主要基于 Sharding-JDBC 1.5.0 正式版 1. 概述 2. DeleteStatement 3. #p

数据库中间件 Sharding-JDBC 源码分析 —— SQL 解析(二)之SQL解析

关注微信公众号:[芋艿的后端小屋]有福利: RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表 RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址 您对于源码的疑问每条留言都将得到认真回复.甚至不知道如何读源码也可以请教噢. 新的源码解析文章实时收到通知.每周更新一篇左右. 认真的源码交流微信群. 1. 概述 2. SQLParsingEngine 3. SQLParser SQL解析器 3.2.1 #parse