一、子程序

  • 子程序是指被命名的PL/SQL块,这种块可以带有参数,可以在不同应用中多次调用
  • PL/SQL有两种类型的子程序:过程函数
    • 过程用于执行特定操作,而函数则用于返回特定数据
      • 过程与函数的比较
  • 都使用IN模式的参数传入数据,OUT模式的参数返回数据
  • 输入参数都可以接收默认值,都可以传值
  • 调用时的实参都可以使用位置表示法或名称表示法
  • 都有声明部分,执行部分和异常处理部分
    • 一般而言如果需要返回多个值或者不返回值,就使用过程
    • 如果只需要返回一个值,就使用函数
    • 虽然函数带OUT模式的参数也能返回多个值,但是一般都认为这种方法属于不好的编程习惯或风格
    • 过程一般用于执行一个指定的动作,函数一般用于计算和返回一个值

      1、过程的创建与使用

      过程的整体结构

      1. 过程一般用于执行一个指定的操作,可以将常用的特定操作封装成过程
      2. CREATE [OR REPLACE] PROCEDURE procedure_name
      3. (argument1 [mode1] datatype1, argument2 [mode2] datatype2, ...)
      4. IS [AS]
      5. 声明部分
      6. BEGIN
      7. 执行部分
      8. EXCEPTION
      9. 异常处理部分
      10. END;

      创建一个简单的过程

      1. --创建一个简单的过程
      2. create or replace procedure pro_out_put as
      3. begin
      4. dbms_output.put_line('这是一个输出过程!');
      5. end;

      删除一个过程

      1. --删除一个过程
      2. drop procedure pro_query_putsal;

      创建一个带有OUT参数的过程

      ```sql —创建一个带有OUT参数的过程 create or replace procedure pro_out_putsal(v_no in emp.empno%type,out_sal out number) as begin select sal into out_sal from emp where empno = v_no; exception
      1. when no_data_found then
      2. dbms_output.put_line('找不到该员工!');
      end;

— 调用过程 declare v_no emp.empno%type; —输入参数 v_sal emp.sal%type; —输出参数 begin v_no:=&no; pro_out_putsal(v_no,v_sal); dbms_output.put_line(‘该员工薪水是’||v_sal); end;

  1. <a name="SAR38"></a>
  2. #### 创建一个带有IN OUT参数的过程
  3. ```sql
  4. --创建一个带有IN OUT参数的过程
  5. create or replace procedure pro_inout_putdata(param_num in out number)
  6. as
  7. begin
  8. select sal into param_num from emp where empno = param_num;
  9. end;
  10. --这个参数既是输入参数,又可以作为输出参数
  11. declare
  12. inout_num number;
  13. begin
  14. inout_num:=&no;
  15. pro_inout_putdata(inout_num);
  16. dbms_output.put_line('工资是:'||inout_num);
  17. end;

创建一个多参数传递的过程

  1. --使用过程时多参传递
  2. create or replace procedure
  3. pro_add_dept(v_deptno number,v_dname varchar2,v_loc varchar2)
  4. as
  5. begin
  6. insert into dept values(v_deptno,v_dname,v_loc);
  7. end;
  8. --多参数过程调用 ,位置传递
  9. call pro_add_dept(70,'研发部','北京');
  10. --多参数过程调用 ,名称传递
  11. call pro_add_dept(v_deptno=>80,v_dname=>'运维部',v_loc=>'杭州');

2、函数的创建与使用

函数的整体结构

  1. CREATE [OR REPLACE] FUNCTION function_name
  2. (argument1 [model] datatype1,
  3. argument2 [mode2] datatype2,
  4. ...)
  5. RETURN datatype
  6. IS|AS
  7. 声明部分
  8. BEGIN
  9. 执行部分
  10. EXCEPTION
  11. 异常处理部分
  12. END;

创建一个简单的函数

  1. --创建一个简单函数
  2. create or replace function fun_random_value
  3. return number --返回值类型
  4. as
  5. v_num number; --返回参数及类型
  6. begin
  7. v_num:=floor(dbms_random.value(1,10)); --生成一个[1-10)的数赋值给返回的参数
  8. return v_num; --返回参数
  9. end;
  10. -- 调用函数
  11. declare
  12. num number; --声明一个参数去接收函数生成的随机数
  13. begin
  14. num:=fun_random_value();
  15. dbms_output.put_line('一个随机数:'||num);
  16. end;

创建一个带有输入参数的函数

  1. --创建一个带有输入参数的函数
  2. create function getDept(eno number) return varchar2
  3. as
  4. deptName dept.dname%type;
  5. begin
  6. select dname into deptName from dept,emp where dept.deptno = emp.deptno and empno =eno;
  7. return deptname;
  8. end;
  9. --根据传入的员工表的员工id,得到部门表中该员工部门的地址
  10. select getDept(7788) from dual;
  11. --根据传入的员工表的员工id,得到部门表中该员工部门的地址
  12. declare
  13. deptName varchar(30);
  14. begin
  15. deptName:=getDept(7788);
  16. dbms_output.put_line(deptName);
  17. end;

创建一个带有输出参数的函数

  1. --创建一个带有输出参数的函数
  2. create function getDept_out_address(eno number,deptName out varchar2)
  3. return varchar2 --声明返回值类型
  4. as
  5. --定义一个参数用于接收函数返回的结果
  6. v_address varchar2(40);
  7. begin
  8. select dName,loc into deptName,v_address from dept,emp where dept.deptno=emp.deptno and empno =eno;
  9. return v_address;
  10. end;
  11. --调用函数
  12. declare
  13. address varchar2(30);
  14. deptName varchar2(20);
  15. noid number; --声明常量,变量,不可赋值
  16. begin
  17. noid:=7654; --在这赋值
  18. address:=getDept_out_address(noid,deptName);
  19. dbms_output.put_line(noid||'部门名称:'||deptName);
  20. dbms_output.put_line('部门地址:'||address);
  21. end;

3、包的创建与使用

  • 创建包
    • 用于组合逻辑相关的PL/SQL类型,PL/SQL项和PL/SQL子程序
    • 通过使用PL/SQL包,不仅可以简化应用设计,提高应用性能,还可以实现信息隐藏,子程序重载等功能
    • 包规范包体两部分组成
    • 当创建包时,首先需要先创建包规范,然后再创建包体
      • 包规范相当于接口,包体相当于接口的实现
  • 创建包规范

    • 包规范是包与应用程序之间的接口,用于定义包的公用组件,包括常量,变量,游标,过程和函数
    • 在包规范中所定义的公用组件不仅可以在包内引用,而且还可以由其他的子程序引用
    • 创建包规范时U需要注意的是:为了实现信息隐藏,不应该将所有的组件全部放在包规范处定义,而应该只定义公用组件

      包的整体结构

      1. ========创建包规范的结构=======
      2. CREATE [OR REPLACE] PACKAGE 包名(package_name
      3. IS|AS
      4. public type and item declarations
      5. subprogram specifications
      6. END 包名(package_name);
      7. ~~~~~~~~~~~~~~~~~~~~~~~~
      8. create or replace package dbutil_package is --包规范开始
      9. pi constant number(10,7):=3.1415926; --定义一个常量PI
      10. function getarea(radius number) return number; --函数
      11. procedure print_area; --过程
      12. end dbutil_package; --包规范结束
      13. ========创建包体的结构=======
      14. CREATE [OR REPLACE] PACKAGE BODY package_name
      15. IS | AS
      16. private type and item declarations
      17. subprogram bodies
      18. END package_name;
      19. ~~~~~~~~~~~~~~~~~~~~~~~~~
      20. create or replace package body dbutil_package as --包体开始
      21. area number(10); --声明一个用于计算的变量
      22. function getarea(radius number) return number is --函数
      23. begin --与包规范里边定义的常量进行计算
      24. area:=pi*radius*radius;
      25. return area; --返回计算结果
      26. end;
      27. procedure print_area is --一个过程
      28. begin
      29. dbms_output.put_line('圆的面积是:'||area);
      30. end;
      31. end dbutil_package; --包体结束
      32. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~调用包的组件
      33. declare
      34. area number(10,7);
      35. begin
      36. area:=dbutil_package.getarea(3); --调用包体中的函数
      37. dbms_output.put_line('由function返回的面积:'||area);
      38. dbutil_package.print_area; --调用过程
      39. end;

      查看子程序的源码

      1. --显示当前用户的所有子程序及其源代码
      2. select text from user_source where name='PACK_UTIL';

      删除子程序

      1. drop procedure 子程序名;

      查看包源代码

      1. --显示当前用户的包及其源代码
      2. select text from user_source where TYPE='PACKAGE' and name='PACK_UTIL';

      删除包

      如果只删除包体,则可以使用DROP PACKAGE BODY命令
      如果同时删除包规范和包体,则可以使用DROP PACKAGE命令

      二、游标

  • 游标分为显式游标和隐含游标两种

    • 隐含游标用于处理SELECT INTO 和DML语句
    • 显式游标则用于处理SELECT语句返回的多行数据
  • 使用显式游标
    • 定义游标:crusor cursor_name is select_statement;
    • 打开游标:open cursor_name;
    • 提取数据:fetch cursor_name into v_data;
    • 关闭游标:close cursor_name;
  • 游标判断条件

    • %isopen
      • 用于判断游标是否已经打开不,打开返回true,否则返回false
    • %found
      • 检查是否从结果集中提取数据,如果提取到数据,返回true,否则返回false
    • %notfound
      • 与%found属性恰好相反,如果提取到数据,则返回值为false,否则返回true
    • %rowcount
      • 返回当前行为已经提取到的实际行数

        显示游标实例

        1. 根据条件查询并输入部门信息
        2. declare
        3. v_dept dept%rowtype;
        4. cursor dept_cursor is select * from dept where deptno>10; --定义游标
        5. begin
        6. open dept_cursor; --打开游标
        7. loop
        8. fetch dept_cursor into v_dept; --提取数据
        9. exit when dept_cursor%notfound; --判断循环退出条件
        10. dbms_output.put_line('编号:'||v_dept.deptno
        11. ||' 名称:'||v_dept.dname||' 地址:'||v_dept.loc);
        12. end loop;
        13. close dept_cursor; --关闭游标
        14. end;

        使用table结构提取游标数据

        1. declare
        2. type v_dept is table of dept%rowtype index by binary_integer;
        3. cursor dept_cursor is select * from dept;
        4. v_dept_table v_dept;
        5. begin
        6. open dept_cursor;
        7. --bulk collect:表示一次性提取全部
        8. fetch dept_cursor bulk collect into v_dept_table;
        9. close dept_cursor;
        10. --从第一个开始存储到最后一个
        11. for i in v_dept_table.first..v_dept_table.last loop
        12. dbms_output.put_line(v_dept_table(i).deptno||'
        13. '||v_dept_table(i).dname||' '||v_dept_table(i).loc);
        14. end loop;
        15. end;

        三、索引

  • 创建索引的原则

    • 一般不需要为数据量很小的表创建索引
    • 对于数据量比较大的表,如果经常需要查询的记录数小于表中所有记录数的10%,则可以考虑为该表创建索引
    • 应该为大部分列值不重复的列创建索引
    • 对于取值范围较大的列(如ename列),应该创建B树索引;对于取值范围较小的列(如sex列),应该创建位图索引
    • 对于包含很多个NULL值,但是经常需要查询所有非NULL值记录的列,应当创建索引
    • 不能在CLOB或BLOB等大对象数据类型的列上创建索引
    • 如果在大部分情况下只需要对表执行只读操作,就可以为该表创建更多的索引以提高查询速度
    • 如果在大部分情况下需要对表执行更新操作,则应该为少创建一些索引,以提高更新速度

      创建索引

      1. create bitmap index 索引名 on 表名(字段名);

      删除索引

      1. drop bitmap index 索引名