PL/SQL 训练11--包

--所谓包,就是把一组PL/SQL的代码元素组织在一个命名空间下。
--一种可以把程序或者其他的PL/SQL元素比如游标、类型、变量的组织结构在一起的结构(包括逻辑结构和物理结构)
--包提供了非常重要的、独一无二的功能,包括隐藏逻辑或者隐藏数据的功能,以及定义和操作“全局”或者
--会话持久数据的能力

 

--可以更容易地增强以及管理应用程序
--整体改善应用程序性能

--改善应用或者内置的薄弱环节

--代码重新编译的需求最小化

--包的演示

--包由两个代码块组成的:规范部分(必须的)和包体部分(可选的,但是一般都会提供)
--规范部分定义的是开发人员该如何使用这个包;包体部分包含罗列在规范部分的程序的实现代码以及还有其他必要的代码元素

--先来看段代码
create table employees(employee_id varchar2(32),first_name varchar2(100),last_name varchar2(100));

procedure process_employee(in_id in employees.employee_id%type)
is 
   l_full_name varchar2(201);
begin 
  
   select e.last_name ||'_'||e.first_name
   into l_full_name
   from employees e 
   where e.employee_id = in_id;
end ;
/

--这段代码有什么问题呢?硬编码、硬编码、硬编码

--为什么不使用包呢?
create or replace package employee_pkg is

  subtype full_name_t is varchar2(200);
  function fullname(in_last  employees.last_name %type,
                    in_first employees.first_name%type) return full_name_t;
  function fullname(in_id in employees.employee_id%type) return full_name_t;
end employee_pkg;
/
--subtype 声明一个新的数据类型,如果要修改长度,很容易

create or replace package body employee_pkg is

  function fullname(in_last  employees.last_name %type,
                    in_first employees.first_name%type) return full_name_t is
  
  begin
    return in_last || ',' || in_first;
  end fullname;
  
  
  function fullname(in_id in employees.employee_id%type) return full_name_t is
    l_full_name full_name_t;
  begin
  
    select fullname(e.last_name, e.first_name)
      into l_full_name
      from employees e
     where e.employee_id = in_id;
  
    return l_full_name;
  exception
    when no_data_found then
      return null;
    when too_many_rows then
      null;
  end fullname;

end employee_pkg;
/

---有关包一些概念

--隐藏信息:移除系统或者应用程序中的可见信息的实践活动
--公有和私有:公有代码是在包的规范部分定义的,任何对这个包有EXECUTE权限的模式都可以使用公有代码
--私有代码是在包的内部定义的并且只对包内可见

--构建包的时候,应该确定哪些元素是公有,哪些是私有的

--包规范:包的规范部分包含了可以被外部程序使用的所有公有元素的声明或者规范

--包体:包体包含了在包规范中所定义的元素的全部实现代码
--包体可以包含私有元素,这些元素不会出现在包声明中,也不能在包外使用

--初始化:可以通过初始化部分对包进行初始化
--会话持久性:持续会话生命周期

---外部、公有、私有

--构建包的规则

--包看起来是个很简单的结构,很容易掌握其语法和规则

--包规范,先来看一个包,之前有一个购物车的实现,我们来看下包的规范部分

create table ma_shopping_car(
 id_ma_shopping_car varchar2(32) default sys_guid() not null ,
 user_id varchar2(32),
 prouct_name varchar2(100),
 product_price number(13,2),
 product_num number
);

create or replace type ma_shopping_obj as  object  (
 id_ma_shopping_car varchar2(32) ,
 user_id varchar2(32),
 prouct_name varchar2(100),
 product_price number(13,2),
 product_num number
);

create or replace package  ma_shopping_pkg is  
   --常量
   c_page_num constant pls_integer := 10;
   --打印购物车
   procedure list_shopping_car(i_user_id in varchar2);
   
   procedure insert_shopping_car(v_product ma_shopping_obj);
   
end ma_shopping_pkg ;
/

--在包规范中可以声明差不多所有数据类型的元素,比如数字、异常、类型、集合。这些也叫做包级别数据

--一般来说,应该尽量避免在包的规范中声明变量,不过声明常量总是“安全的”

---不能在包规范中声明游标变量,游标变量不能做到会话范围内的持久化

--在包规范中可以声明任何的数据结构,比如集合类型,记录类型或者REF CURSOR 类型

--可以在包规范中声明一个过程和函数,但只能在包规范中包含程序的头部,头部必须用分号结尾

--可以在包规范中使用显示游标

--如果在包规范中声明了任意一个过程或者函数,或者声明一个没有查询语句的游标,必须提高一个包体来实现这些代码元素

--可以在包结尾的END语句之后可选的跟上一个包的名字作为标签

--包体

--包体部分包含了实现包规范所有需要的全部代码,包体并不总是必须的。
--当下面的条件为真时才需要包体

--包规范汇中包含了一个带有return子句的游标声明
--包规范中包含了过程或函数声明
--想通过包的初始化单元执行代码

---包规范本身并没有执行单元,只能在包体中实现

create or replace package body ma_shopping_pkg is
  
  type shop_t is table of ma_shopping_obj index by ma_shopping_car.prouct_name%type;
  my_shopping_car shop_t; --私有变量

 procedure init_shopping_car(i_user_id in varchar2) is
  
  begin
    if my_shopping_car.count = 0 then
    
      for v_cur in (select r.id_ma_shopping_car,
                           r.user_id,
                           r.prouct_name,
                           r.product_price,
                           r.product_num
                      from ma_shopping_car r
                     where r.user_id = i_user_id) loop
        --赋值对象
        my_shopping_car(v_cur.prouct_name) := ma_shopping_obj(v_cur.id_ma_shopping_car,
                                                              v_cur.user_id,
                                                              v_cur.prouct_name,
                                                              v_cur.product_price,
                                                              v_cur.product_num);
      end loop;
    
    end if;
  
  end init_shopping_car;
  
  procedure list_shopping_car(i_user_id in varchar2) is
    v_index ma_shopping_car.prouct_name%type;
  begin
    if my_shopping_car.count = 0 then
         init_shopping_car(i_user_id);
    end if;
    v_index := my_shopping_car.first;
    while my_shopping_car.exists(v_index) loop
    
      dbms_output.put_line(my_shopping_car(v_index)
                           .prouct_name || ',价格' ||
                           my_shopping_car(v_index)
                           .product_price || ',购买数量' ||
                           my_shopping_car(v_index).product_num);
    
      v_index := my_shopping_car.next(v_index);
    end loop;
  end list_shopping_car;
  ---其它实现
 
  --购物车中数据持久化
  procedure insert_shopping_car(v_product in ma_shopping_obj) is
  
  begin
    insert into ma_shopping_car
      (id_ma_shopping_car,
       user_id,
       prouct_name,
       product_price,
       product_num)
      select nvl(v_product.id_ma_shopping_car, sys_guid()),
             v_product.user_id,
             v_product.prouct_name,
             v_product.product_price,
             v_product.product_num
        from dual;
  end insert_shopping_car;
end ;
/
---测试
begin

ma_shopping_pkg.insert_shopping_car(ma_shopping_obj(null,'test','IPAD',3200,5));
ma_shopping_pkg.insert_shopping_car(ma_shopping_obj(null,'test','IPHONE',5000,3));
ma_shopping_pkg.insert_shopping_car(ma_shopping_obj(null,'test','BOOK',300,2));

end ;
/
select * from ma_shopping_car;

begin 
   ma_shopping_pkg.list_shopping_car('test');

end ;
/

--包体里可以有声明单元、执行单元、异常处理单元。
--在声明单元中就包括了在包规范中定义的任何一个游标和程序的完整实现
--也包括所有私有元素的定义。只要有一个初始化单元,声明单元可以为空

--包的执行单元也叫做初始化单元,可选的,当会话对这个包进行实例化时会执行这部分代码

--异常处理单元会处理初始化单元抛出的异常

--一个包体可以包括下面这些组合:只有一个声明单元;只有一个执行单元;
--同时有执行单元和异常处理单元;或者同时有声明单元、执行单元以及异常处理单元

--关于声明包级别数据结构的规则和约束同样适用于包体和包规范,比如不能声明一个游标变量

--可以在包体结尾的END语句后面选择性地使用包名标签


--包的初始化
--包可以包含能够持续在整个会话生命期内的数据结构
--当会话第一次使用某个包时,数据库都会对包初始化。

--初始化步骤
--初始化所有包级别的数据,比如数值变脸或者字符串常量
--用变量或者常量声明时所指定的缺省值给它们赋值
--执行初始化单元中的代码块,这个单元是专门为包的初始化设计的,作为前面没这些步骤的补充

--包的初始化单元由位于包结尾的BEGIN语句直到整个包体最后的END语句中所有语句组成

create or replace package body ma_shopping_pkg is

  type shop_t is table of ma_shopping_obj index by ma_shopping_car.prouct_name%type;
  my_shopping_car shop_t; --私有变量
  
  function getName return varchar2;
  
  c_test varchar2(100) :=getName();
  
  function getName return varchar2 is
  begin
    dbms_output.put_line('oh hear');
    return 'hello';
  end getName;


  ---其它实现
  procedure init_shopping_car(i_user_id in varchar2) is
  
  begin
    if my_shopping_car.count = 0 then
    
      for v_cur in (select r.id_ma_shopping_car,
                           r.user_id,
                           r.prouct_name,
                           r.product_price,
                           r.product_num
                      from ma_shopping_car r
                     where r.user_id = i_user_id) loop
        --赋值对象
        my_shopping_car(v_cur.prouct_name) := ma_shopping_obj(v_cur.id_ma_shopping_car,
                                                              v_cur.user_id,
                                                              v_cur.prouct_name,
                                                              v_cur.product_price,
                                                              v_cur.product_num);
      end loop;
    
    end if;
  
  end init_shopping_car;

  procedure list_shopping_car(i_user_id in varchar2) is
    v_index ma_shopping_car.prouct_name%type;
  begin
    if my_shopping_car.count = 0 then
      init_shopping_car(i_user_id);
    end if;
    v_index := my_shopping_car.first;
    while my_shopping_car.exists(v_index) loop
    
      dbms_output.put_line(my_shopping_car(v_index)
                           .prouct_name || ',价格' ||
                           my_shopping_car(v_index)
                           .product_price || ',购买数量' ||
                           my_shopping_car(v_index).product_num);
    
      v_index := my_shopping_car.next(v_index);
    end loop;
  end list_shopping_car;

  --购物车中数据持久化
  procedure insert_shopping_car(v_product in ma_shopping_obj) is
  
  begin
    insert into ma_shopping_car
      (id_ma_shopping_car,
       user_id,
       prouct_name,
       product_price,
       product_num)
      select nvl(v_product.id_ma_shopping_car, sys_guid()),
             v_product.user_id,
             v_product.prouct_name,
             v_product.product_price,
             v_product.product_num
        from dual;
  end insert_shopping_car;
begin

  dbms_output.put_line('这里正在进行初始化');

end ma_shopping_pkg;
/

begin 
   ma_shopping_pkg.list_shopping_car('test');
end ;
/

begin 
   ma_shopping_pkg.list_shopping_car('test');
end ;
/

--为什么要进行初始化

--执行复杂的初始化逻辑
--缓存静态的会话信息
--避免初始化时的意外情况
--初始化失败?

create or replace package pkg_err is 

   function get return varchar2;
end pkg_err;
/
create or replace package body pkg_err is

  v varchar2(1) := 'abc';
  function get return varchar2 is
  begin
    return v;
  end get;

begin
  dbms_output.put_line('oh,I am hear');
exception
  when others then
    dbms_output.put_line('interesting,there is error');--这个异常只捕获声明单元的异常
end pkg_err;
/
---发生了什么?
begin 
  dbms_output.put_line(pkg_err.get);
end ;
/

--再一次
begin 
  dbms_output.put_line(nvl(pkg_err.get,'noting'));
end ;
/

---使用包数据

--包数据是定义在包级别的变量和常量组成的,也就是不是在包的某个函数和过程中定义的

--包数据的作用范围:整个包

--会话持久性


---如果包数据是在包体中声明的,会话期间持久化,私有的,只能被包内部成员使用

--如果包数据是在包规范中声明的,则这个数据会在整个会话生命期内持久化
--可以被那些对这个包有EXECUTE权限的程序使用。

--如果在一个包过程中打开一个游标,这个游标会在整个会话生命期中一直保持这打开和可用的状态

--包变量可以跨越事务边界传递数据


---在一个ORACLE会话内全局可见

--包数据不能在多个会话之间共享使用


--全局公有数据:在包规范中声明的所有数据结构都属于全局公有的数据结构
--也就是位于包以外的程序都可以使用它们

---包游标

--可以在包中声明一个游标(显示游标),既可以在包体中声明也可以在包规范中声明。
--这个游标的状态会在整个会话中保持
--可以在一个程序里打开游标,在另外一个程序里提取数据,在第三个程序里关闭游标

--灵活但有可能导致问题

 

---声明包游标

--在包规范中声明一个显示游标,有两种方法
--1.在包规范中声明一个带查询语句的完整游标。和在一个局部PL/SQL块中声明游标完全一样
--2. 只声明一个游标的头部,不带有查询语句。这种方式下,查询是在包体中定义的
--对于第二种方式,只声明了游标头,必须在游标定义加上一个return子句

--return语句只能利用数据库中表的%ROWTYPE属性定义的记录或者用户自定的记录类型
create or replace package ma_shopping_pkg2 is

  CURSOR cur_my_car(i_user in ma_shopping_car.user_id%type) is
    select * from ma_shopping_car t where t.user_id = i_user;
    
  cursor cur_my_car1(i_user in ma_shopping_car.user_id%type) return ma_shopping_car%rowtype;

  type ma_shop_rec is record(
   product_name varchar2(100),
   total_price number 
  );
  cursor cur_my_total(i_user in ma_shopping_car.user_id%type) return ma_shop_rec;
end ma_shopping_pkg2;
/

create or replace package body ma_shopping_pkg2 is
  cursor cur_my_car1(i_user in ma_shopping_car.user_id%type) return ma_shopping_car%rowtype is
    select * from ma_shopping_car t where t.user_id = i_user;

  cursor cur_my_total(i_user in ma_shopping_car.user_id%type) return ma_shop_rec is
    select t.prouct_name, sum(t.product_num * t.product_price)
      from ma_shopping_car t
     where t.user_id = i_user
     group by t.prouct_name;
end ma_shopping_pkg2;
/
---使用包游标,在前面课程里,我们已经学习了游标,包游标的使用语法没什么特别的

DECLARE
  v_car ma_shopping_car%rowtype;
BEGIN

  open ma_shopping_pkg2.cur_my_car('test');
  loop
    exit when ma_shopping_pkg2.cur_my_car%notfound;
    fetch ma_shopping_pkg2.cur_my_car
      into v_car;
    dbms_output.put_line(v_car.prouct_name);
  end loop;
  close ma_shopping_pkg2.cur_my_car;

END;
/
--示例中的游标是在包规范中声明的,所以游标的作用范围不局限于任何PL/SQL块,比如
BEGIN

  open ma_shopping_pkg2.cur_my_car('test');

END;
/
DECLARE
  v_car ma_shopping_car%rowtype;
BEGIN

  loop
    exit when ma_shopping_pkg2.cur_my_car%notfound;
    fetch ma_shopping_pkg2.cur_my_car
      into v_car;
    dbms_output.put_line(v_car.prouct_name);
  end loop;

END;
/
BEGIN
  close ma_shopping_pkg2.cur_my_car;

END;
/

---关于游标的持久化

--永远不要假设一个包游标已经关闭
--永远不要假设一个包游标是打开的
--确保每次使用完包游标后,总是显示的关闭它。就算在异常处理部分也要加上这个逻辑
--这样可以保证无论从哪个点退出程序,游标都会关闭

 

---包的串行化

--包数据有些缺陷
--可能导致无法预料的情况发生
--如果数据都存在包级别的结构中,程序就可能消耗大量的物理内存

--可以使用SERIALLY_REUSABLE编译指令对包标识成串行的可重用,包规范和包体都必须同时出现
--即对这种包的状态的生命周期可以从整个会话减少到对包的一个程序调用

--示例


create or replace package  ma_shopping_pkg3 is  

   pragma serially_reusable ;
   procedure fill_shopping_car;
   procedure list_shopping_car;

end ma_shopping_pkg3 ;
/


create or replace package body ma_shopping_pkg3 is
  pragma serially_reusable;
  type shop_t is table of ma_shopping_car%rowtype index by varchar2(32);

  my_shopping_car shop_t;

  procedure fill_shopping_car is
  
  begin
    for v in (select * from ma_shopping_car) loop
      my_shopping_car(v.prouct_name) := v;
    end loop;
  end fill_shopping_car;
  procedure list_shopping_car is
    v_index ma_shopping_car.prouct_name%type;
  begin
    if my_shopping_car.count = 0 then
      dbms_output.put_line('nothing');
    else
      v_index := my_shopping_car.first;
      while my_shopping_car.exists(v_index) loop
        dbms_output.put_line(my_shopping_car(v_index)
                             .prouct_name || ',价格' ||
                             my_shopping_car(v_index)
                             .product_price || ',购买数量' ||
                             my_shopping_car(v_index).product_num);
      
        v_index := my_shopping_car.next(v_index);
      end loop;
    end if;
  end list_shopping_car;

end ma_shopping_pkg3;
/
--两种不同的调用方式,看看发生了什么
begin 
ma_shopping_pkg3.fill_shopping_car;
ma_shopping_pkg3.list_shopping_car;

end ;
/
begin 
ma_shopping_pkg3.fill_shopping_car;

end ;
/
begin 
ma_shopping_pkg3.list_shopping_car;
end ;
/

--何时使用包

--封装数据操作
--与其让开发人员自己写SQL语句,不如给这些语句提供一个接口。表API或者事务API

create or replace package  ma_shopping_pkg is  
   --常量
   c_page_num constant pls_integer := 10;
   --打印购物车
   procedure list_shopping_car(i_user_id in varchar2);
   --获取购物车信息
   procedure  init_shopping_car(i_user_id in varchar2);
   
   procedure insert_shopping_car(v_product ma_shopping_obj);
   
   function update_shopping_car(v_product ma_shopping_obj) return number;
   
   procedure save_shopping_car ;
end ma_shopping_pkg ;
/


create or replace package body ma_shopping_pkg is

  type shop_t is table of ma_shopping_obj index by ma_shopping_car.prouct_name%type;
  my_shopping_car shop_t; --私有变量

  procedure list_shopping_car(i_user_id in varchar2) is
    v_index ma_shopping_car.prouct_name%type;
  begin
    if my_shopping_car.count = 0 then
      init_shopping_car(i_user_id);
    end if;
    v_index := my_shopping_car.first;
    while my_shopping_car.exists(v_index) loop
    
      dbms_output.put_line(my_shopping_car(v_index)
                           .prouct_name || ',价格' ||
                           my_shopping_car(v_index)
                           .product_price || ',购买数量' ||
                           my_shopping_car(v_index).product_num);
    
      v_index := my_shopping_car.next(v_index);
    end loop;
  end list_shopping_car;
  ---其它实现
  procedure init_shopping_car(i_user_id in varchar2) is
  
  begin
    if my_shopping_car.count = 0 then
    
      for v_cur in (select r.id_ma_shopping_car,
                           r.user_id,
                           r.prouct_name,
                           r.product_price,
                           r.product_num
                      from ma_shopping_car r
                     where r.user_id = i_user_id) loop
        --赋值对象
        my_shopping_car(v_cur.prouct_name) := ma_shopping_obj(v_cur.id_ma_shopping_car,
                                                              v_cur.user_id,
                                                              v_cur.prouct_name,
                                                              v_cur.product_price,
                                                              v_cur.product_num);
      end loop;
    
    end if;
  
  end init_shopping_car;
  --购物车中数据持久化
  procedure insert_shopping_car(v_product in ma_shopping_obj) is
  
  begin
    insert into ma_shopping_car
      (id_ma_shopping_car,
       user_id,
       prouct_name,
       product_price,
       product_num)
      select nvl(v_product.id_ma_shopping_car, sys_guid()),
             v_product.user_id,
             v_product.prouct_name,
             v_product.product_price,
             v_product.product_num
        from dual;
  end insert_shopping_car;

  function update_shopping_car(v_product ma_shopping_obj) return number is
  begin
    update ma_shopping_car r
       set r.prouct_name  = v_product.prouct_name,
           r.product_price = v_product.product_price,
           r.product_num   = v_product.product_num
     where r.id_ma_shopping_car = v_product.id_ma_shopping_car;
    return sql%rowcount;
  end update_shopping_car;

  procedure save_shopping_car is
    v_index ma_shopping_car.prouct_name%type;
  begin
    if my_shopping_car.count <> 0 then
    
      v_index := my_shopping_car.first;
      while my_shopping_car.exists(v_index) loop
        if update_shopping_car(my_shopping_car(v_index)) = 0 then
          insert_shopping_car(my_shopping_car(v_index));
        end if;
      end loop;
    
    end if;
  end;
end ma_shopping_pkg;
/

---避免对直接量的硬编码
--把常量放在包中,并给这些直接量一个名字,避免在每个程序中的硬编码。

 

--改善内置功能的可用性
--ORACLE自己的一些工具,比如UTL_FILE,还离期待的有所差距。基于这些工具构建我们自己的包
--可以尽可能地弥补这些问题

 

--把逻辑上相关的功能组织在一起
--如果有一堆过程和函数都是围绕着程序的某方面内容的,那么把它们放在一个包里
--更容易管理代码

--缓存会话的静态数据从而盖上应用程序性能
--利用包的持久化来缓存静态数据,可以改善应用程序的响应时间

 

------------------------------------------------------------------------------------------------

1 异常包
CREATE OR REPLACE PACKAGE TEST1.exception_logs_pkg
IS
   PROCEDURE exception_logs_p (
      i_option_users        IN exception_logs.option_users%TYPE,
      i_method_name         IN exception_logs.method_name%TYPE,
      i_exception_line      IN exception_logs.exception_line%TYPE,
      i_exception_code      IN exception_logs.exception_code%TYPE,
      i_exception_message   IN exception_logs.exception_message%TYPE--i_exception_level    IN exception_logs.exception_level%TYPE
      );
END exception_logs_pkg;
/
CREATE OR REPLACE PACKAGE BODY TEST1.exception_logs_pkg
IS
   /******************************************************************************
      NAME:       exception_logs_pkg
      PURPOSE:

      REVISIONS:
      Ver        Date        Author           Description
      ---------  ----------  ---------------  ------------------------------------
      1.0        2016-03-08    hongquan        1. Created this package body.
   ******************************************************************************/
   PROCEDURE exception_logs_p (
      i_option_users        IN exception_logs.option_users%TYPE,
      i_method_name         IN exception_logs.method_name%TYPE,
      i_exception_line      IN exception_logs.exception_line%TYPE,
      i_exception_code      IN exception_logs.exception_code%TYPE,
      i_exception_message   IN exception_logs.exception_message%TYPE--i_exception_level    IN exception_logs.exception_level%TYPE
      )
   IS
      PRAGMA AUTONOMOUS_TRANSACTION;
      v_sysdate           DATE DEFAULT SYSDATE;
      v_exception_level   NUMBER DEFAULT 0;
 BEGIN     
  BEGIN
   SELECT   exception_level
      INTO v_exception_level
     FROM exception_level
      WHERE exception_code=i_exception_code;
      
   EXCEPTION 
   WHEN OTHERS THEN
    v_exception_level:=3;
    END ;
    
   BEGIN
      INSERT INTO exception_logs (option_users,
                                  method_name,
                                  exception_time,
                                  exception_line,
                                  exception_code,
                                  exception_message,
                                  exception_level)
           VALUES (i_option_users,
                   i_method_name,
                   v_sysdate,
                   i_exception_line,
                   i_exception_code,
                   i_exception_message,
                   v_exception_level);

      COMMIT;
   EXCEPTION
      WHEN OTHERS
      THEN
         ROLLBACK;
   END;
  END;
END exception_logs_pkg;
/
2 购物车模块包
CREATE OR REPLACE package TEST1.ma_shopping_pkg is  
   --常量
   c_page_num constant pls_integer := 10;
   --打印购物车
   procedure list_shopping_car(i_user_id in varchar2);
   --获取购物车信息
   procedure  init_shopping_car(i_user_id in varchar2);
   
   procedure insert_shopping_car(v_product ma_shopping_obj);
   
   function update_shopping_car(v_product ma_shopping_obj) return number;
   
   procedure save_shopping_car ;
end ma_shopping_pkg ;
/
CREATE OR REPLACE package body TEST1.ma_shopping_pkg is

  type shop_t is table of ma_shopping_obj index by ma_shopping_car.prouct_name%type;
  my_shopping_car shop_t; --私有变量

  procedure list_shopping_car(i_user_id in varchar2) is
    v_index ma_shopping_car.prouct_name%type;
  begin
    if my_shopping_car.count = 0 then
      init_shopping_car(i_user_id);
    end if;
    v_index := my_shopping_car.first;
    while my_shopping_car.exists(v_index) loop
    
      dbms_output.put_line(my_shopping_car(v_index)
                           .prouct_name || ',价格' ||
                           my_shopping_car(v_index)
                           .product_price || ',购买数量' ||
                           my_shopping_car(v_index).product_num);
    
      v_index := my_shopping_car.next(v_index);
    end loop;
  end list_shopping_car;
  ---其它实现
  procedure init_shopping_car(i_user_id in varchar2) is
  
  begin
    if my_shopping_car.count = 0 then
    
      for v_cur in (select r.id_ma_shopping_car,
                           r.user_id,
                           r.prouct_name,
                           r.product_price,
                           r.product_num
                      from ma_shopping_car r
                     where r.user_id = i_user_id) loop
        --赋值对象
        my_shopping_car(v_cur.prouct_name) := ma_shopping_obj(v_cur.id_ma_shopping_car,
                                                              v_cur.user_id,
                                                              v_cur.prouct_name,
                                                              v_cur.product_price,
                                                              v_cur.product_num);
      end loop;
    
    end if;
  
  end init_shopping_car;
  --购物车中数据持久化
  procedure insert_shopping_car(v_product in ma_shopping_obj) is
  
  begin
    insert into ma_shopping_car
      (id_ma_shopping_car,
       user_id,
       prouct_name,
       product_price,
       product_num)
      select nvl(v_product.id_ma_shopping_car, sys_guid()),
             v_product.user_id,
             v_product.prouct_name,
             v_product.product_price,
             v_product.product_num
        from dual;
  end insert_shopping_car;

  function update_shopping_car(v_product ma_shopping_obj) return number is
  begin
    update ma_shopping_car r
       set r.prouct_name  = v_product.prouct_name,
           r.product_price = v_product.product_price,
           r.product_num   = v_product.product_num
     where r.id_ma_shopping_car = v_product.id_ma_shopping_car;
    return sql%rowcount;
  end update_shopping_car;

  procedure save_shopping_car is
    v_index ma_shopping_car.prouct_name%type;
  begin
    if my_shopping_car.count <> 0 then
    
      v_index := my_shopping_car.first;
      while my_shopping_car.exists(v_index) loop
        if update_shopping_car(my_shopping_car(v_index)) = 0 then
          insert_shopping_car(my_shopping_car(v_index));
        end if;
      end loop;
    
    end if;
  end;
end ma_shopping_pkg;
/

3 订单派工模块包

create or replace package distribute_orders is
  function get_area_dealer(in_area varchar2) return varchar2 result_cache;
  procedure distribute_orders;
end distribute_orders;
/


create or replace package body distribute_orders is
  type cache_t is table of order_area_dealer_config.deal_user%type index by varchar2(32);
  g_cache cache_t;
  function get_area_dealer(in_area varchar2) return varchar2 result_cache RELIES_ON(order_area_dealer_config) is
    v_dealer order_area_dealer_config.deal_user%type;
  begin
    if g_cache.exists(in_area) then
      v_dealer := g_cache(in_area);
    else
      select deal_user
        into v_dealer from order_area_dealer_config where distribution_area = in_area and rownum = 1;
      g_cache(in_area) := v_dealer;
    end if;
    return v_dealer;
  end get_area_dealer;
  procedure distribute_orders is
    cursor cur_ma_orders is
      select * from ma_orders where is_distributed = 'N';
    v_dealer order_area_dealer_config.deal_user%type;
  begin
    for v in cur_ma_orders loop
      v_dealer := get_area_dealer(v.distribution_area);
      insert into order_distribute
        (id_ma_orders, deal_user)
      values
        (v.id_ma_orders, v_dealer);
      update ma_orders t
         set t.is_distributed = 'Y'
       where t.id_ma_orders = v.id_ma_orders;
    end loop;
  commit;
  end;
end distribute_orders;
/     

 

posted @ 2019-03-28 15:14  春困秋乏夏打盹  阅读(282)  评论(0编辑  收藏  举报