第十章:函数&包

来源:互联网 发布:php调用网页代码 编辑:程序博客网 时间:2024/05/30 21:58

--函数的创建和使用
--函数像过程一样,也存储在数据库中。
--两者之间的重要区别是,函数是能够返回单个值
--的PL/SQL语句块。

--创建函数的语法如下所示: 
--CREATE [OR REPLACE]FUNCTION  function_name(parameter list)
  RETURN datatype
  { IS | AS }
     [ declare_section ] 
  BEGIN
     <body>
     RETURN (return_value);
  END;

--第一个函数
create or replace function show_description(i_course_no course.course_no%type)
return varchar2 
is
  v_description varchar2(30);
begin
  --找到指定课程的名称,存到变量中
  select description
    into v_description
    from course
    where course_no=i_course_no;

  return v_description;--返回课程名称
end show_description;


--函数调用:
--1)在plsql块或者存储过程中调用
--PLS-00221:'SHOW_DESCRIPTION' is not aprocedure or is undefined
begin
  show_description(430);
end;

--在plsql中调用函数,要求一定要对函数的返回值做处理。
begin
  dbms_output.put_line(show_description(430));
end;

--或者:
declare
  v_description varchar2(30);
begin
  v_description := show_description(430);

  dbms_output.put_line(v_description);
end;

--2)使用select语句调用。有很多限制
select show_description(430)from dual;


--2:检查给定的学生编号是否合法
create or replace function id_is_good(i_student_idnumber
return boolean 
is
  v_id_cnt number(1);  
begin
  select count(*)
    into v_id_cnt
    from student
    where student_id=i_student_id;

  return (1=v_id_cnt);
exception
  when others then
    return false;  
end id_is_good;


--使用select语句调用以上函数
--ORA-06553: PLS-382: expressionis of wrong type
select id_is_good(100)from dual;

--因为函数的返回类型是boolean型,sql不支持

begin
  if id_is_good(100) then
    dbms_output.put_line('good');
  else
    dbms_output.put_line('bad');
  end if;
end;


--创建和使用包。非常重要。
--可以把包看做一个容器,里面存放过程、函数、变量、类型
--等东西。使用多个包,就可以将过程和函数进行分类存放。

--创建一个包分两个部分:
--1)包规范(包接口):其中包含过程和函数的头部声明,
--但是不包含他们的具体实现。包规范中的所有对象都叫做
--公共对象,意味着他们可以被包之外的代码访问。
--2)包体:其中包含过程和函数的头部声明和具体实现。
--也可以包含私有对象(过程、函数、变量等)的声明。

--创建学生管理包
create or replace package manage_students is

  -- Author  : ADMINISTRATOR
  -- Created :2014-06-309:57:43
  -- Purpose : 学生管理包

  procedure find_sname(i_student_idin number,
                       o_first_name out varchar2,
                       o_last_name  out varchar2);

  function id_is_good(i_student_idnumber) return boolean;
end manage_students;

--当包规范创建并编译完毕后,客户端已经可以访问了。
declare
  o_first_name varchar2(20);
  o_last_name varchar2(20);
begin
  if manage_students.id_is_good(102) then
    manage_students.find_sname(102,o_first_name,o_last_name );  
  end if;
end;

--执行以上代码,会抛出异常:
ORA-04067: not executed, package body "STUDENT.MANAGE_STUDENTS" doesnot exist
ORA-06508: PL/SQL: couldnot find program unit being called: "STUDENT.MANAGE_STUDENTS"

--这是正常的,因为过程和函数还没有具体实现,当然
--不能执行。


--实现包体
create or replace package body manage_students is
  --过程实现
  procedure find_sname(i_student_idin number,
                       o_first_name out varchar2,
                       o_last_name  out varchar2)is
  begin
    select first_name, last_name
      into o_first_name, o_last_name
      from student
     where student_id = i_student_id;
  exception
    when no_data_found then
      DBMS_OUTPUT.PUT_LINE('Error in finding student_id: ' || i_student_id);
  end find_sname;

  --函数实现
  function id_is_good(i_student_idnumber) return booleanis
    v_id_cnt number(1);
  begin
    select count(*)
      into v_id_cnt
      from student
     where student_id = i_student_id;

    return(1= v_id_cnt);
  exception
    when others then
      return false;
  end id_is_good;
end manage_students;

--再执行以下代码,成功
declare
  o_first_name varchar2(20);
  o_last_name varchar2(20);
begin
  if manage_students.id_is_good(102) then
    manage_students.find_sname(102,o_first_name,o_last_name );  
  end if;
end;


--创建私有对象
--如果只在包体中定义某对象,则它是私有的。
--该包外部的任何程序不能直接访问私有元素

--在manage_students包规范定义中添加一个新的过程的声明(如下),并重新编译包规范
     PROCEDURE display_student_count;
END manage_students;

--
create or replace package manage_students is

  -- Author  : ADMINISTRATOR
  -- Created :2014-06-309:57:43
  -- Purpose : 学生管理包

  procedure find_sname(i_student_idin number,
                       o_first_name out varchar2,
                       o_last_name  out varchar2);

  function id_is_good(i_student_idnumber) return boolean;

  procedure display_student_count;

end manage_students;


--包体:
create or replace package body manage_students is
  --过程实现
  procedure find_sname(i_student_idin number,
                       o_first_name out varchar2,
                       o_last_name  out varchar2)is
  begin
    select first_name, last_name
      into o_first_name, o_last_name
      from student
     where student_id = i_student_id;
  exception
    when no_data_found then
      DBMS_OUTPUT.PUT_LINE('Error in finding student_id: ' || i_student_id);
  end find_sname;

  --函数实现
  function id_is_good(i_student_idnumber) return booleanis
    v_id_cnt number(1);
  begin
    select count(*)
      into v_id_cnt
      from student
     where student_id = i_student_id;

    return(1= v_id_cnt);
  exception
    when others then
      return false;
  end id_is_good;


  --定义私有函数。该函数只能被本包中的
  --其它过程或函数调用
  function student_count_priv 
    return number 
  is
    v_count number;
  begin
    select count(*)
      into v_count
      from student;

    return v_count;
  end student_count_priv;    

  procedure display_student_count
   is
   begin
     --调用私有函数
     dbms_output.put_line(student_count_priv);   
   end display_student_count;


end manage_students;


--测试
begin
  manage_students.display_student_count;
end;

--PLS-00302: component'STUDENT_COUNT_PRIV' must be declared
begin
  dbms_output.put_line(manage_students.student_count_priv);
end;


--使用包变量
--将变量声明放在包规范中,那么该变量就是一个全局变量。
--可以被任何代码来访问。可以实现
--同一个会话的多个过程之间交换(共享)数据。

--包的初始化部分
--在包体的末尾。当一个用户会话第1次调用一个包时,
--该包的初始化部分的代码就会执行。只执行这一次

--给manage_students包添加包变量
create or replace package manage_students is

  -- Author  : ADMINISTRATOR
  -- Created :2014-06-309:57:43
  -- Purpose : 学生管理包

  --包变量
  v_current_date date;

  procedure find_sname(i_student_idin number,
                       o_first_name out varchar2,
                       o_last_name  out varchar2);

  function id_is_good(i_student_idnumber) return boolean;

  procedure display_student_count;

end manage_students;


--在包体的初始化部分给包变量赋值
create or replace package body manage_students is
  --过程实现
  procedure find_sname(i_student_idin number,
                       o_first_name out varchar2,
                       o_last_name  out varchar2)is
  begin
    select first_name, last_name
      into o_first_name, o_last_name
      from student
     where student_id = i_student_id;
  exception
    when no_data_found then
      DBMS_OUTPUT.PUT_LINE('Error in finding student_id: ' || i_student_id);
  end find_sname;

  --函数实现
  function id_is_good(i_student_idnumber) return booleanis
    v_id_cnt number(1);
  begin
    select count(*)
      into v_id_cnt
      from student
     where student_id = i_student_id;

    return(1= v_id_cnt);
  exception
    when others then
      return false;
  end id_is_good;


  --定义私有函数。该函数只能被本包中的
  --其它过程或函数调用
  function student_count_priv 
    return number 
  is
    v_count number;
  begin
    select count(*)
      into v_count
      from student;

    return v_count;
  end student_count_priv;    

  procedure display_student_count
   is
   begin
     dbms_output.put_line(student_count_priv);   
   end display_student_count;

begin --这是包的初始化部分,对每个会话只执行一次   
  select sysdate
    into v_current_date
    from dual;
  dbms_output.put_line('包初始化完毕');    
end manage_students;


--测试:新开会话窗口,分别执行以下代码2次。观察输出。
begin
  dbms_output.put_line(manage_students.v_current_date); 
end;

--在包中使用游标变量。很重要
--游标变量就是一个refcursor类型的变量。

--定义refcursor类型的语法:
--TYPE  ref_type_name  IS  REF  CURSOR
   [RETURN 记录类型];

--如果定义refcursor类型时带了return子句,那么
--该类型的游标变量就叫做强类型的游标变量。如果
--没有带return子句,那么该类型的游标变量就叫做
--弱类型的游标变量

--弱类型的游标变量可以指向任何一个select查询的
--结果集。而强类型的游标变量可以指向的查询,要求
--select列表必须满足记录类型的内部结构。

--游标变量在包中主要用作过程的输出参数,用来从
--服务器向客户端返回一个结果集。

CREATE  OR  REPLACE  PACKAGE  course_pkg  AS
     TYPE  course_rec_typ  IS RECORD
         (first_name  student.first_name%TYPE,
           last_name  student.last_name%TYPE,
          course_no  course.course_no%TYPE,
          description  course.description%TYPE,
          section_no  section.section_no%TYPE
          );

      TYPE  course_cur  ISREF CURSOR  RETURN  course_rec_typ;

      PROCEDURE   get_course_list
           (p_student_id  NUMBER ,
            p_instructor_id  NUMBER ,
            course_list_cv  INOUT  course_cur);  --声明游标变量
END  course_pkg;


CREATE OR REPLACE PACKAGE BODY  course_pkg  AS
    PROCEDURE get_course_list
         (p_student_id  NUMBER ,
          p_instructor_id  NUMBER ,
          course_list_cv  IN OUT course_cur)
    IS
    BEGIN
         IF  p_student_id  ISNULL  AND p_instructor_id  ISNULL  THEN
              OPEN  course_list_cv  FOR   --打开游标变量
                    SELECT 'Please choose a student-'  First_name,
                                  'instructor combination' Last_name,
                          NULL  course_no,
                          NULL  description,
                          NULL  section_no
                        FROM  dual;
         ELSIF  p_student_id   ISNULL  THEN
OPEN  course_list_cv  FOR   --打开游标变量
            SELECT  s.first_name  first_name,
                           s.last_name  last_name,
                           c.course_no  course_no,
                           c.description  description,
                           se.section_no  section_no
              FROM  instructor i, student s,
                           section se, coursec, enrollment e
            WHERE  i.instructor_id= p_instructor_id
                 AND  i.instructor_id= se.instructor_id
                 AND  se.course_no= c.course_no
                 AND  e.student_id= s.student_id
                 AND  e.section_id= se.section_id
       ORDER BY  c.course_no, se.section_no;
     ELSIF  p_instructor_id  ISNULL  THEN

OPEN  course_list_cv  FOR   --打开游标变量
                SELECT  i.first_name  first_name,
                               i.last_name  last_name,
                               c.course_no  course_no,
                               c.description  description,
                               se.section_no  section_no
                   FROM  instructor i, student s,
                                section se, coursec, enrollment e
                 WHERE  s.student_id= p_student_id
                      AND  i.instructor_id= se.instructor_id
                      AND  se.course_no= c.course_no
                      AND  e.student_id= s.student_id
                      AND  e.section_id= se.section_id
            ORDER BY  c.course_no, se.section_no;
       END IF;
   END  get_course_list;
END  course_pkg;


--在sqlplus中使用游标变量调用过程
--sqlplus中的variable命令专门用来定义绑定变量,
--其类型可以是游标变量。

variable course_cv refcursor;

--注意,以上语句命令窗口不支持

--调用过程
execute course_pkg.get_course_list(102,null,:course_cv);

--打印绑定变量的值。可以看到结果集输出
print :course_cv;

--注意,print命令会自动关闭游标变量。再次执行,出错:

exec  course_pkg.get_course_list(NULL,102, :course_cv);

print :course_cv;


--如果过程的参数有一个弱类型的游标变量,那么
--在包中可以不写type ...isref cursor语句,而是
--直接使用系统预先定义的一个refcursor类型,其
--名字是sys_refcursor.
0 0