1 前言
何谓递归,在程序编码行业中,递归是经常要听到的名词,但真正有使用递归程序解决用户需求的程序编码人员却少之又少。
所谓递归,简而言之就是应用程序自身调用自身,以实现层次数据结构的查询和访问。
2 递归程序的作用
我们有处理具有层次结构的数据结构时,往往需要使用递归的概念。
举个实例:<以下我们均以BOM表作为实例进行解释,不再谈及相对抽象的文字>
BOM表,在自称为“ERP”的系统中,BOM采用的是有限定层次结构的树形结构,如:第一层表示成品、第二层表示半成品、第三层表示在第二层之下的半成品,如此推类,在层次限定的前提下,该程序通常没有使用递归的概念进行编程,BOM表的各个层级程序处理往往使用硬代码编程,造成程序复杂,可移植性差,维护麻烦等缺点。
应用递归的概念后,BOM表的层级可以无限,对BOM表的处理,如计算材料成本或原材料清单等,则可以使用简洁的代码来实现。
3 递归程序的特点
递归程序的共同特点:
(1)代码中必定包括一个循环;
(2)循环中调用自身程序;
(3)循环的条件为递归终止的条件
如此构成一个完整的递归程序
4 递归程序的应用实例
--4.1 数据表结构
--BOM表样例,里面包含较为简单的BOM层次结构
Create Table Test_Bom (
ItemKey VarChar2(10) Not Null--产品(或材料)主键,关联产品基本档,在此不进行具体描述。原应为数值类型,在此以字符类型表示,以方便查看和说明。
,MaterialKey VarChar2(10) Not Null--半成品(或材料)主键,关联产品基本档,在此不进行具体描述。原应为数值类型,在此以字符类型表示,以方便查看和说明。
,Qty Number(6,2) Not Null--单位用量
,constraint PK_Test_Bom PRimary key (ItemKey,MaterialKey)
);
--4.2 测试数据
--A由A1和A2构成
Delete From Test_Bom;
Insert Into Test_Bom (MaterialKey, ItemKey,Qty) Values ('a1','a',1);
Insert Into Test_Bom (MaterialKey, ItemKey,Qty) Values ('a2','a',2);
--A1由A11和A12构成
Insert Into Test_Bom (MaterialKey, ItemKey,Qty) Values ('a11','a1',3);
Insert Into Test_Bom (MaterialKey, ItemKey,Qty) Values ('a12','a1',4);
--A11由A111和A112构成
Insert Into Test_Bom (MaterialKey, ItemKey,Qty) Values ('a111','a11',5);
Insert Into Test_Bom (MaterialKey, ItemKey,Qty) Values ('a112','a11',6);
--A12由A121和A122以及A123构成
Insert Into Test_Bom (MaterialKey, ItemKey,Qty) Values ('a121','a12',7);
Insert Into Test_Bom (MaterialKey, ItemKey,Qty) Values ('a122','a12',8);
Insert Into Test_Bom (MaterialKey, ItemKey,Qty) Values ('a123','a12',9);
--A2由A21和A22构成
Insert Into Test_Bom (MaterialKey, ItemKey,Qty) Values ('a21','a2',10);
Insert Into Test_Bom (MaterialKey, ItemKey,Qty) Values ('a22','a2',11);
--A21由A212和A1构成
Insert Into Test_Bom (MaterialKey, ItemKey,Qty) Values ('a212','a21',12);
Insert Into Test_Bom (MaterialKey, ItemKey,Qty) Values ('a1','a21',5);
Commit;
--由此看出真正需要购买的原材料是:a111,a112,a121,a122,a123,a22,a212
--4.3 需求
--要列出产品A的材料清单,以方便核算成本和请购材料。
--4.4 实现方法
--4.4.1 创建临时表,用于存储计算结果
--为方便查看,本例使用常规数据表,实际应用过程中,应使用临时表,避免用户之间的干扰
--create Global Temporary table Test_Bom_MaterialList_TP(
MaterialKey VarChar2(10) Not Null
-- ,Qty Number(10) Not Null
--)
-- On Commit PreServe Rows
--/
create table Test_Bom_MaterialList_TP(
MaterialKey VarChar2(10) Not Null
,Qty Number(10) Not Null
)
/
--4.4.2 创建程序包,用于计算原材料用量
CREATE OR REPLACE Procedure SP_EXPortMaterial
(P_ItemKey In Varchar2,P_Qty Number)
is
--=============================================
--创建存放游标数据的记录类型
--=============================================
type Material_rec is record(
ItemKey Test_Bom.ItemKey%type,
MaterialKey Test_Bom.MaterialKey%type,
Qty Test_Bom.Qty%Type);
--=============================================
--创建存放游标记录的记录集对象
--=============================================
Material_Record Material_Rec;
--=============================================
--创建游标,游标的记录集数据类型为Material_Rec
--=============================================
Type Material_Cur is ref cursor Return Material_Rec;
--=============================================
--创建游标对象
--=============================================
Material_Cursor Material_Cur;
L_IsNextLayer Numeric;--存放是否有下一层物料的变量
L_MaterialQty Numeric;--当前物料的单位用量
begin
--=============================================
--打开游标,传入数据
--=============================================
open Material_CurSor for
select ItemKey --主产品KEY
,MaterialKey --物料KEY
,Qty --物料标准用量
from Test_Bom
Where ItemKey=P_ItemKey;
--=============================================
--将数据传入BOM_MaterialList_TP临时数据表
--=============================================
--将游标数据推入Material_Record记录集对象/
Fetch Material_CurSor Into Material_Record;
While Material_Cursor%Found --判定游标是否为空
Loop
--判定当前物料编号是否有下一层物料
Select Count(0) Into L_IsNextLayer
From Test_Bom
Where ItemKey=Material_Record.MaterialKey;
If Not L_IsNextLayer=0 Then
--假如有下层物料,则执行再调用SP_ExportMaterial<自身程序,递归点>
SP_ExportMaterial
(Material_Record.MaterialKey
,P_Qty*Material_Record.Qty);
Else
--将当前游标数据传入Test_Bom_MaterialList_TP临时数据表
Insert Into Test_Bom_MaterialList_TP
(MaterialKey,Qty)
Values(Material_Record.MaterialKey
,P_Qty*Material_Record.Qty);
--将游标数据推入Material_Record记录集对象
End if;
Fetch Material_CurSor Into Material_Record;
End Loop;
Commit;
end SP_ExportMaterial;
--4.5 测试
Delete From Test_Bom_MaterialList_TP; --删除临时表数据
SP_ExportMaterial('a1',1); --执行计算程序
Select * from Test_Bom_MaterialList_TP;