ERP中递归获取物料清单(BOM)方法
物料清单(Bill of Material, BOM),是制造执行系统 (manufacturing execution system,MES)和企业资源计划系统(Enterprise Resource Planning,ERP)里面必不可少的一个表,这个表最大的特点就是由无数棵树组成的森林,而且树之间还有交叉(共通枝叶),数据量比较大,在数据库二维表中以递归方式存储,对这个表的检索和操作是系统中比较重要的一个组成部分。下面简单写写数据的检索,主要涉及Sql server2008和oracle11中的检索方式。
1、Sql server2008 中的两种检索模式:
1) 存储过程方式,递归有32层的限制,而且游标很麻烦,没有oracle的那种行变量,很罗嗦。
![](/assets/blank.gif)
![](/assets/blank.gif)
1 ALTER PROCEDURE [DBO].[GETBOMTREE]( 2 @UPCD CHAR(25), 3 @LEVEL INT 4 ) AS 5 BEGIN 6 SET NOCOUNT ON; 7 DECLARE @FACCD CHAR(12), @UPPERCD CHAR(25), 8 @SEQ INT,@LOWERCD CHAR(25),@NEWLEVEL INT; 9 10 IF @LEVEL = 0 11 BEGIN 12 SET @LEVEL=1; 13 END 14 15 DECLARE BOM_CURSOR CURSOR LOCAL FOR --注意是LOCAL游标 16 SELECT FAC_CD,UPPER_ CD,USE_SEQ,ISNULL(LOWER_ CD,'') 17 FROM T_BOM 18 WHERE UPPER_CD = @UPCD ; 19 20 OPEN BOM_CURSOR; 21 22 FETCH NEXT FROM BOM_CURSOR 23 INTO @FACCD,@UPPERCD,@SEQ,@LOWERCD ; 24 25 -- 26 IF @@FETCH_STATUS <>0 27 BEGIN 28 CLOSE BOM_CURSOR; 29 DEALLOCATE BOM_CURSOR; 30 31 RETURN ; 32 END 33 34 -- 35 WHILE @@FETCH_STATUS = 0 36 BEGIN 37 --插入数据进入临时表(实际为物理表,每次执行时删除该表数据,还要增加终端号或用户名区分多用户操作,这里省略) 38 INSERT INTO T_BOM_TMP( 39 FAC_CD,UPPER_CD,USE_SEQ,LOWER_CD,LEVEL 40 ) VALUES ( 41 @FACCD,@UPPERCD,@SEQ,@LOWERCD,@LEVEL 42 ); 43 44 --调用本身 45 SET @NEWLEVEL=@LEVEL + 1; 46 EXEC DBO.GETBOMTREE @LOWERCD,@NEWLEVEL; 47 48 FETCH NEXT FROM BOM_CURSOR 49 INTO @FACCD, @UPPERCD,@SEQ,@LOWERCD ; 50 END 51 52 CLOSE BOM_CURSOR; 53 DEALLOCATE BOM_CURSOR; 54 END
2) CTE方式,该方式无递归限制,并且代码简洁,是第一选择。
![](/assets/blank.gif)
![](/assets/blank.gif)
1 WITH BOM_CTE AS 2 3 ( 4 5 SELECT *,1 AS ILEVEL FROM T_BOM WHERE 6 7 FAC_CD = 'FAC01' AND --工厂代码 8 9 UPPER_CD = 'AAA' --要查找的根节点 10 11 UNION ALL 12 13 SELECT P.*,D.ILEVEL + 1 AS ILEVEL 14 15 FROM T_BOM AS P 16 17 INNER JOIN BOM_CTE AS D ON 18 19 P.FAC_CD = D.FAC_CD AND 20 21 P.UPPER_CD = D.LOWER_CD 22 23 ) 24 25 SELECT * FROM BOM_CTE
2、ORACLE11中的两种方式
1)存储过程,我只写包体的主要部分(由于没有环境,代码没有经过测试)
![](/assets/blank.gif)
![](/assets/blank.gif)
1 --插入临时表数据 2 3 PROCEDURE INS_BOM_TMP(V_ROW_BOM T_BOM_TMP%ROWTYPE) IS BEGIN 4 INSERT INTO T_BOM_TMP VALUES V_ROW_BOM; 5 END INS_BOM_TMP; 6 7 --主要递归过程 8 9 PROCEDURE GET_BOM(V_FAC_CD T_BOM.FAC_CD%TYPE, 10 11 V_UPPDER_CD T_BOM.UPPER_CD%TYPE, 12 13 LEVEL NUMBER(10,0)) IS 14 15 CURSOR V_BOM_CURSOR IS 16 SELECT * FROM T_BOM WHERE FAC_CD = V_FAC_CD AND UPPER_CD= V_UPPDER_CD; 17 V_CUR V_BOM_CURSOR%ROWTYPE; 18 V_ROW T_BOM_TMP%ROWTYPE; 19 BEGIN 20 FOR V_CUR IN V_BOM_CURSOR LOOP 21 V_ROW.FAC_CD := V_CUR.FAC_CD; 22 V_ROW.UPPER_ITEM_CD := V_CUR.UPPER_CD; 23 V_ROW.USE_SEQ := V_CUR.USE_SEQ; 24 25 V_ROW.LOWER_ITEM_CD := V_CUR.LOWER_CD; 26 V_ROW.LEVEL := LEVEL; 27 INS_BOM_TMP (V_ROW); --插入临时表 28 29 GET_BOM(V_FAC_CD, V_CUR.LOWER_CD, LEVEL+1); --递归调用 30 END LOOP; 31 EXCEPTION 32 WHEN OTHERS THEN 33 --定义一个错误函数记录错误日志 34 WRITE_ERR_LOG(PACKAGE_ID,'GET_BOM', SQLERRM); 35 END GET_BOM;
个人很喜欢oracle的存储过程编写,变量声明方式很好,函数也很丰富,尤其行变量很方便。
2) ORACLE的递归语句(相当简洁)
![](/assets/blank.gif)
![](/assets/blank.gif)
SELECT FAC_CD,UPPER_CD,USE_SEQ,LOWER_CD,LEVEL --LEVEL是关键字 FROM T_BOM WHERE LEVEL <= 3 AND FAC_CD='FAC01' START WITH UPPER_CD='AAA' --根节点 CONNECT BY PRIOR LOWER_CD=UPPER_CD --从根到叶 --CONNECT BY LOWER_CD=PRIOR UPPER_CD --从叶到根 ORDER SIBLINGS BY USE_SEQ --子节点内部排序
3、LINQ方式获取BOM(Linq to Sql,递归)
![](/assets/blank.gif)
![](/assets/blank.gif)
1 void Main() 2 3 { 4 5 var query=GetBom("AAA"); 6 7 Console.WriteLine("Upper Code"); 8 9 query.ToList().ForEach(q=>Console.WriteLine("{0}",q.UPPER_CD)); 10 11 } 12 13 14 15 public IEnumerable<T_BOM> GetBom(string UpperCD) 16 17 { 18 19 var query = from b in T_BOM 20 21 where b.UPPER_CD == UpperCD 22 23 select b; 24 25 26 27 return query.ToList().Concat(query.ToList().SelectMany(t => GetBom(t.LOWER_CD))); 28 29 }
在我本地机上用该方法在从10万多条数据中获取321条的一棵树,用时2秒多(LinqPad中执行的结果),无论用自己写的存储过程或CTE方式,都只有0.2秒左右(Management studio中执行的结果)。另外,观察LINQ生成的SQL语句,发现数据重复获取,由于效率低下,我采用了EF中用存储过程方式,不过总有种“如鲠在喉”的感觉,希望哪位大师能告诉我EF下如何递归大数据量的问题,不胜感激。
补充:上面的代码我也是网上找的一段代码,由于假期有点空,就仔细看了看代码,稍微变更一下,去掉了重复获取数据,性能有很大提高,0.6秒多,代码如下
![](/assets/blank.gif)
![](/assets/blank.gif)
1 void Main() 2 { 3 var query=GetBom("AAA"); 4 Console.WriteLine("Upper Code"); 5 query.ToList().ForEach(q=>Console.WriteLine("{0}",q.UPPER_CD)); 6 } 7 8 public IEnumerable<T_BOM> GetBom(string UpperCD) 9 { 10 var list = (from b in T_BOM 11 where b.UPPER_CD == UpperCD 12 select b).ToList(); 13 14 return list.Concat(list.SelectMany(t => GetBom(t.LOWER_CD))); 15 }
以上代码性能有很大提高,但总归有一定的欠缺。不过技术是死的,设计是活的,可以扩充“深度”字段和“页标识”字段来减少代码的复杂度和获取数据的灵活度,比如叶子是已知的情况,就不再去递归检索,在Web项目中还可以通过AJAX展开枝叶。不过要注意,如果树有交叉,“深度”字段是无意义的。也有的设计采用触发器自动更新深度字段,该方式虽然比较好,但用ORM框架总是希望数据库无关的。当然,各种技术和设计的优缺点都有,仁者见仁,智者见智罢了。
4、附上Tree代码生成函数(winform程序,VB.net代码,非递归)
![](/assets/blank.gif)
![](/assets/blank.gif)
1 Private Sub CreateBomTree(dtBom As DataTable, tnParent As Windows.Forms.TreeNode) 2 Dim tnChild As System.Windows.Forms.TreeNode 3 Dim iLevel As Int16 = 1 '层号 4 For Each dr As DataRow In dtBom.Rows 5 tnChild = New Windows.Forms.TreeNode 6 tnChild.Name = dr("LOWER_CD").ToString().Trim() 7 tnChild.Text = dr("USE_SEQ") & " " & dr("LOWER_DESC").ToString().Trim() 8 If dr("LEVEL") = iLevel Then 9 tnParent.Nodes.Add(tnChild) 10 ElseIf dr("LEVEL") > iLevel Then 11 iLevel = dr("LEVEL") 12 tnParent = tnParent.LastNode 13 tnParent.Nodes.Add(tnChild) 14 Else 15 For i As Int16 = dr("LEVEL") To iLevel - 1 16 tnParent = tnParent.Parent 17 Next 18 19 iLevel = dr("LEVEL") 20 tnParent.Nodes.Add(tnChild) 21 End If 22 Next 23 End Sub
转载于:https://www.cnblogs.com/Kylin98/archive/2012/10/06/2712928.html
ERP中递归获取物料清单(BOM)方法相关推荐
- linux java获取路径_linux中java获取路径的方法
linux中java获取路径的方法 发布时间:2020-05-06 11:11:26 来源:亿速云 阅读:700 作者:小新 今天小编给大家分享的是linux中java获取路径的方法,相信很多人都不太 ...
- Windows中快速获取文件目录的方法
计算机使用时间越长,硬盘中保存的文件就会越来越多,如果不对这些众多的文件创建合适的目录,日后要寻找某个文件将变得非常困难.为了提高文件定位的效率创建方便快捷的文件目录,下面有如下几则快速生成文件目录的 ...
- 安卓中为了获取context的方法和区别(getContext,getActivity,this,mainActivity.this等)
最近在着手做项目的时候,发现在需要context的时候,不同的类或者位置中需要用不同的方法聊火气context对象.在百度谷歌查找资料以及问周围的大神之后,总结了大概如下的内容: 1.MainActi ...
- php中count获取多维数组长度的方法
转自:http://www.jb51.net/article/57021.htm 本文实例讲述了php中count获取多维数组长度的实现方法.分享给大家供大家参考.具体分析如下: 先来看看下面程序运行 ...
- 【DB笔试面试597】在Oracle中,获取执行计划有哪几种方法?
♣题目部分 在Oracle中,获取执行计划有哪几种方法? ♣答案部分 一般来说,有如下几种获取执行计划的方式: 1.AUTOTRACE方式 AUTOTRACE是Oracle自带的客户端工具SQL*Pl ...
- ERP中BOM的数据库设计与实现
引言在企业资源计划(Enterprise Resource Planning,ERP)中, 物料清单(Bill of Materials,BOM)是系统中的最基本资料,用来描述产品的零部件组成和零部件 ...
- ERP中BOM变更操作的简单操作
BOM变更 BOM变更在ERP中时不时的会发生,BOM变更不仅仅是只是BOM版本变更一下就结束了,往往旧版本的BOM相关正在生产中的工单如何处理才是最重要,也是最麻烦的事情. 生产过程节点这张图进行理 ...
- php中多维数组的长度,php中count获取多维数组长度的方法
这篇文章主要介绍了php中count获取多维数组长度的方法,实例分析了数组的原理并总结了数组长度计算的方法,非常具有实用价值,需要的朋友可以参考下 本文实例讲述了php中count获取多维数组长度的实 ...
- java 中lock,java中lock获取锁的四种方法
在java接口中会存放着许多方法,方便线程使用时的直接调用.对于lock接口大家都不陌生,我们已经初步对概念进行了理解.那么在获取锁的方法上想必还不是很清楚.下面我们就lock获取锁的四种方法分别进行 ...
最新文章
- Python中最好用的命令行参数解析工具
- boost::combine相关的测试程序
- 如何拼局域网所有ip_求助 家庭网络如何布线让所有网口在同一个局域网内
- mfa助听器设备能否在android,助听器的蓝牙功能到底有什么用,购买的价格,以及购买时要注意什么等问题...
- SQL Server安全性和基于策略的管理–高级条件
- 3dmax中为人物添加动作的流程
- Python入门--字符串的连接和替换,replace,join
- Unix/Linux笔记全集
- 使用EasyRecovery简单修复视频
- 电机控制park变换公式推导
- 北大青鸟c语言课后答案,北大青鸟C语言实现.ppt
- 我的上岸之旅——深圳工业大学
- 一级计算机脚注怎么加,word怎么插入脚注 word添加脚注图文教程
- 系统的用户分析方法及分析内容
- 34、CSS高频前端面试题之CSS基础
- Android Material Design简单使用 http://www.cnblogs.com/android-blogs/p/5632103.html
- 直流斩波电路在matlab中的建模与仿真,基于MATLAB/Simulink的直流斩波电路分析
- 【2022 CCF BDCI 文心大模型创意项目】DIY绘本
- 单点登录、统一认证解决方案(一)
- linux 路由转发 ipv6,IPv6路由