本文系转载子ITPUB,如果有侵犯您权益的地方,烦请及时的告知与我,我即刻将停止侵权行为:
网址:http://www.itpub.net/thread-1020586-1-1.html
http://www.itpub.net/thread-1020772-3-1.html
http://www.itpub.net/thread-1020712-1-1.html
LEVEL Parent Child Parent Qty Child Qty
1 A B 1 3
2 B C 2 3
3 C D 5 6
4 D E 1 2
1 A Z 1 3
A是成品
B,C,D是半成品
E,Z是原材料
从上面一个比例关系可以计算出,做一个A最终需要10.8个E和3个Z,
也就是能看到下面的结果
ParentChild QTY
A E 10.8
A Z 3
我想知道有没有什么办法通过一个SQL语句来实现这个功能。
测试表:
CREATE TABLE BOM (PARENT VARCHAR2(10),CHILD VARCHAR2(10),P_QTY NUMBER, C_QTY NUMBER);
INSERT INTO BOM VALUES ('A','B',1,3);
INSERT INTO BOM VALUES ('B','C',2,3);
INSERT INTO BOM VALUES ('C','D',5,6);
INSERT INTO BOM VALUES ('D','E',1,2);
INSERT INTO BOM VALUES ('A','Z',1,3);
COMMIT;
1、使用SQL
1 SELECT P, D, SUM(QTY) 2 FROM (SELECT P, C, D, POWER(10, SUM(LOG(10, QTY))) ASQTY 3 FROM (SELECT DISTINCTP, 4 C, 5 SUBSTR(C, -1, 1) D, 6 REGEXP_SUBSTR(C, '[^,]+', 1, LEVEL), 7 TO_NUMBER(REGEXP_SUBSTR(Q, '[^*]+', 1, LEVEL)) ASQTY 8 FROM (SELECT CONNECT_BY_ROOT PARENT ASP, 9 SUBSTR(SYS_CONNECT_BY_PATH(CHILD, ','), 2) ASC, 10 1 || SYS_CONNECT_BY_PATH(C_QTY / P_QTY, '*') ASQ 11 FROMBOM 12 WHERE CONNECT_BY_ISLEAF = 1 13 START WITH PARENT = 'A' 14 CONNECT BY PARENT =PRIOR CHILD) C 15 CONNECT BY LEVEL <= LENGTH(REGEXP_REPLACE(Q, '[^*]', '')) + 1 16 ORDER BY 1, 2) TT 17 GROUP BYP, C, D) FF 18 GROUP BY P, D
2、使用NEWID提供的聚合求积函数
解决思路:
1.把A的每个叶子找出来;
2.顺着叶子往根,一路作乘法上去。
1 SELECTCHILD, 2 (SELECT PROD_AGG(C_QTY) /PROD_AGG(P_QTY) 3 FROMBOM 4 CONNECT BY PRIOR PARENT =CHILD 5 START WITH CHILD = INNER.CHILD --从每个叶子开始 6 ) ASC_QTY 7 FROM (SELECT BOM.*, CONNECT_BY_ISLEAF ASIS_LEAF 8 FROMBOM 9 CONNECT BY PRIOR CHILD =PARENT 10 START WITH PARENT = 'A') 11 INNER WHERE IS_LEAF = 1 --这个条件找出所有的叶子
其中PROD_AGG 见:
http://www.itpub.net/thread-1020772-1-1.html
CHILD C_QTY
---------- ----------
E 10,8
Z 3
不用PRO_AGG的方法:
1 不用 PROD_AGG的办法: 2 SELECTCHILD 3 ,(SELECT POWER(10,SUM(LOG(10,C_QTY)))/POWER(10,SUM(LOG(10,P_QTY))) 4 FROMBOM 5 CONNECT BY PRIOR PARENT=CHILD 6 START WITH CHILD = inner.CHILD --从每个叶子开始 7 ) ASC_QTY 8 FROM (SELECT BOM.* 9 ,CONNECT_BY_ISLEAF ASIS_LEAF 10 FROMBOM 11 CONNECT BY PRIOR CHILD =PARENT 12 START WITH PARENT='A' 13 ) inner 14 WHERE IS_LEAF=1; --这个条件找出所有的叶子
3、使用PL/SQL的方法:可以将1替换为connect_by_isleaf:如果为叶子节点,则该函数的值1,否则为0,刚好可以替代1,
1 SELECTCHILD, GET_EXPRESSION_RSLT(CON_QTY) 2 FROM (SELECTCHILD, 3 LEVELM, 4 1 || SYS_CONNECT_BY_PATH(C_QTY / P_QTY, '*') CON_QTY 5 FROMBOM 6 WHERE CONNECT_BY_ISLEAF = 1 7 START WITH PARENT = 'A' 8 CONNECT BY PARENT = PRIOR CHILD) A
结果:
1E10.8
2F3.85714285714285714285714285714285714287
3Z3
自定义函数:
1 CREATE OR REPLACE FUNCTION GET_EXPRESSION_RSLT(I_EXPRESSION VARCHAR2) RETURN VARCHAR2 IS 2 /************************************************************ 3 * 函数名称:GET_EXPRESSION_RSLT 4 * 功能描述:获取指定的表达式的结果 5 * 参数:I_EXPRESSION :表达式 例如:1*2*3 6 * 编 写 人:XXX 7 * 编写时间:XXXX-XX-XX 8 * 修改记录: 9 *************************************************************/ 10 RETURNSTR VARCHAR2(500) := ''; 11 EXECSQL VARCHAR2(4000) := ''; 12 BEGIN 13 EXECSQL := 'SELECT ' || I_EXPRESSION || 'FROM DUAL'; 14 EXECUTEIMMEDIATE (EXECSQL) 15 INTORETURNSTR; 16 RETURNRETURNSTR; 17 END;
如果要看A用了每个B,C,D,E
则只需要将CONNECT_BY_SILEFT=1去掉即可
也可以使用下面的语句:
1 SELECTCHILD, dbms_aw.eval_number(CON_QTY) 2 FROM (SELECTCHILD, 3 LEVELM, 4 1 || SYS_CONNECT_BY_PATH(C_QTY / P_QTY, '*') CON_QTY 5 FROMBOM 6 WHERE CONNECT_BY_ISLEAF = 1 7 START WITH PARENT = 'A' 8 CONNECT BY PARENT = PRIOR CHILD) A
其中dbms_aw.eval_number这个函数是用来解析字符串函数的
或者使用with函数
1 WITH H AS 2 (SELECT SYS_CONNECT_BY_PATH(CHILD, '/') NAVPATH, 3 CHILD, 4 QUANTITY QTY, 5 ISLEAF 6 FROMITEMHIER 7 START WITH PARENT = 'ASSY001' 8 CONNECT BY PRIOR CHILD =PARENT) 9 SELECTH1.NAVPATH, H1.CHILD,( 10 SELECT /*EXP(SUM(LN(H2.QTY))),*/ 11 POWER(10, SUM(LOG(10, QTY))) 12 FROMH H2 13 WHERE INSTR(H1.NAVPATH, H2.NAVPATH) = 1) QTY 14 FROMH H1 15 WHERE ISLEAF = 1