楼主: jvkojvko

[精华] 对BOM展开并计算最终使用量的解答

[复制链接]
论坛徽章:
33
劳斯莱斯
日期:2013-08-08 14:01:23三菱
日期:2013-09-28 10:16:06一汽
日期:2013-11-19 17:01:11凯迪拉克
日期:2013-12-07 17:11:282014年新春福章
日期:2014-02-18 16:42:02马上有房
日期:2014-02-18 16:42:02itpub13周年纪念徽章
日期:2014-09-27 14:20:21itpub13周年纪念徽章
日期:2014-10-08 15:13:38懒羊羊
日期:2015-03-04 14:52:112015年新春福章
日期:2015-03-06 11:58:18
31#
发表于 2008-7-14 14:14 | 只看该作者
原帖由 jvkojvko 于 2008-7-13 20:57 发表


不怕,我是男的

如此更可怕

使用道具 举报

回复
论坛徽章:
9607
土豪章
日期:2013-12-31 14:11:39土豪章
日期:2013-12-31 14:11:39阿森纳
日期:2013-06-03 17:00:31阿森纳
日期:2013-10-11 09:27:58法拉利
日期:2013-12-27 15:20:30林肯
日期:2013-12-27 15:19:09法拉利
日期:2013-12-27 15:20:30法拉利
日期:2013-12-27 15:20:30法拉利
日期:2013-12-27 15:20:30法拉利
日期:2013-12-27 15:20:30
32#
 楼主| 发表于 2008-7-14 15:40 | 只看该作者
呵呵,等着呢

使用道具 举报

回复
论坛徽章:
9607
土豪章
日期:2013-12-31 14:11:39土豪章
日期:2013-12-31 14:11:39阿森纳
日期:2013-06-03 17:00:31阿森纳
日期:2013-10-11 09:27:58法拉利
日期:2013-12-27 15:20:30林肯
日期:2013-12-27 15:19:09法拉利
日期:2013-12-27 15:20:30法拉利
日期:2013-12-27 15:20:30法拉利
日期:2013-12-27 15:20:30法拉利
日期:2013-12-27 15:20:30
33#
 楼主| 发表于 2008-7-14 21:41 | 只看该作者
高手呢?

使用道具 举报

回复
论坛徽章:
33
劳斯莱斯
日期:2013-08-08 14:01:23三菱
日期:2013-09-28 10:16:06一汽
日期:2013-11-19 17:01:11凯迪拉克
日期:2013-12-07 17:11:282014年新春福章
日期:2014-02-18 16:42:02马上有房
日期:2014-02-18 16:42:02itpub13周年纪念徽章
日期:2014-09-27 14:20:21itpub13周年纪念徽章
日期:2014-10-08 15:13:38懒羊羊
日期:2015-03-04 14:52:112015年新春福章
日期:2015-03-06 11:58:18
34#
发表于 2008-7-14 23:20 | 只看该作者
原帖由 jvkojvko 于 2008-7-14 21:41 发表
高手呢?

不是完了么,还要什么高手,
我的问题是OK了

使用道具 举报

回复
论坛徽章:
520
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
35#
发表于 2008-7-14 23:45 | 只看该作者
嗯,必须承认小马哥的SQL有点难懂,我斗胆来给你的最新成果挑点毛病:

Select p,d,Sum(qty) From (
Select p,c,d,power(10,Sum(Log(10,qty))) As qty From (
Select Distinct   P,c,SUBSTR(C,-1,1) D,regexp_substr(c,'[^,]+',1,Level), to_number(regexp_substr(Q,'[^*]+',1,Level)) As qty From (
Select CONNECT_BY_ROOT paret As P ,substr(SYS_CONNECT_BY_PATH(CHID,','),2) As C,1||SYS_CONNECT_BY_PATH(CQTY/PQTY,'*') As Q From tmp
Where CONNECT_BY_ISLEAF =1
Start With paret ='A'
Connect By  paret =  Prior CHID
) C
connect by  Level<=length(regexp_replace(Q,'[^*]',''))+1
Order By 1,2
)tt
Group  By p,c,d)ff
Group By p,d


这个SQL里面是把CQTY/PQTY逐层拼接然后又解析出来。碰巧例子中都是可以除尽的,如果碰上一个像 2/3 的你就有误差了。不如把CQTY和PQTY分开,最后算好总乘积再相除。
你在第三行有个SUBSTR(C,-1,1) D, 其实这个就是CHID, 你最好在最里层的SELECT多写一个CHID, 然后在有用到D的地方直接引用CHID。
你在第九行有个Level<=length(regexp_replace(Q,'[^*]',''))+1, 其实右边就是最里层的LEVEL, 你最好在最里层的SELECT多写一个LEVEL AS L, 这里改为Level<= L+1.
第十行来了个排序,完全无用。
最后,我不喜欢Level<=length(regexp_replace(Q,'[^*]',''))+1这样的写法,它的连接没有用到任何PRIOR条件,前后层完全无关,只要集合里行数稍多一点,右边的表达式稍微大一点,那么这个SELECT返回的行数就呈几何级数的上涨。虽然你后来用DISTINCT过滤掉了,但中间结果的开销可不小。

使用道具 举报

回复
论坛徽章:
9607
土豪章
日期:2013-12-31 14:11:39土豪章
日期:2013-12-31 14:11:39阿森纳
日期:2013-06-03 17:00:31阿森纳
日期:2013-10-11 09:27:58法拉利
日期:2013-12-27 15:20:30林肯
日期:2013-12-27 15:19:09法拉利
日期:2013-12-27 15:20:30法拉利
日期:2013-12-27 15:20:30法拉利
日期:2013-12-27 15:20:30法拉利
日期:2013-12-27 15:20:30
36#
 楼主| 发表于 2008-7-15 07:42 | 只看该作者
原帖由 newkid 于 2008-7-14 23:45 发表
嗯,必须承认小马哥的SQL有点难懂,我斗胆来给你的最新成果挑点毛病:

Select p,d,Sum(qty) From (
Select p,c,d,power(10,Sum(Log(10,qty))) As qty From (
Select Distinct   P,c,SUBSTR(C,-1,1) D,regexp_substr(c,'[^,]+',1,Level), to_number(regexp_substr(Q,'[^*]+',1,Level)) As qty From (
Select CONNECT_BY_ROOT paret As P ,substr(SYS_CONNECT_BY_PATH(CHID,','),2) As C,1||SYS_CONNECT_BY_PATH(CQTY/PQTY,'*') As Q From tmp
Where CONNECT_BY_ISLEAF =1
Start With paret ='A'
Connect By  paret =  Prior CHID
) C
connect by  Level


恩,学习了,先看看

使用道具 举报

回复
论坛徽章:
33
劳斯莱斯
日期:2013-08-08 14:01:23三菱
日期:2013-09-28 10:16:06一汽
日期:2013-11-19 17:01:11凯迪拉克
日期:2013-12-07 17:11:282014年新春福章
日期:2014-02-18 16:42:02马上有房
日期:2014-02-18 16:42:02itpub13周年纪念徽章
日期:2014-09-27 14:20:21itpub13周年纪念徽章
日期:2014-10-08 15:13:38懒羊羊
日期:2015-03-04 14:52:112015年新春福章
日期:2015-03-06 11:58:18
37#
发表于 2008-7-15 08:34 | 只看该作者
原帖由 newkid 于 2008-7-14 23:45 发表
嗯,必须承认小马哥的SQL有点难懂,我斗胆来给你的最新成果挑点毛病:

Select p,d,Sum(qty) From (
Select p,c,d,power(10,Sum(Log(10,qty))) As qty From (
Select Distinct   P,c,SUBSTR(C,-1,1) D,regexp_substr(c,'[^,]+',1,Level), to_number(regexp_substr(Q,'[^*]+',1,Level)) As qty From (
Select CONNECT_BY_ROOT paret As P ,substr(SYS_CONNECT_BY_PATH(CHID,','),2) As C,1||SYS_CONNECT_BY_PATH(CQTY/PQTY,'*') As Q From tmp
Where CONNECT_BY_ISLEAF =1
Start With paret ='A'
Connect By  paret =  Prior CHID
) C
connect by  Level

有道理.
两位有看过杨大侠的语句没?

使用道具 举报

回复
论坛徽章:
520
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
38#
发表于 2008-7-15 10:19 | 只看该作者
学习一下杨大侠的SQL: 最后的9i版本

SELECT SUBSTR(MAX(D), LENGTH(MAX(D))) C,
SUBSTR(P, 2, 1),
POWER(10, SUM(LOG(10, Q))) Q
FROM
(
SELECT SYS_CONNECT_BY_PATH(ID, '/') ID,
  SYS_CONNECT_BY_PATH(CHILD, '/') P,
  SYS_CONNECT_BY_PATH(PARENT, '/') D,
  CHILD_QTY/PARENT_QTY Q
FROM T_LEVEL1
START WITH ID IN
(
  SELECT ID FROM
  (
   SELECT ID,
    CASE WHEN LEAD(LEVELS) OVER(ORDER BY RN) > LEVELS THEN 0 ELSE 1 END LEAF
   FROM
   (
    SELECT ROWNUM RN, ID, LEVELS, PARENT, CHILD
    FROM T_LEVEL1
    START WITH LEVELS = 1
    CONNECT BY PRIOR CHILD = PARENT
    ORDER SIBLINGS BY LEVELS
   )
  )
  WHERE LEAF = 1
)
CONNECT BY PRIOR PARENT = CHILD
)
GROUP BY SUBSTR(ID, 2, 1), SUBSTR(P, 2, 1) ;

他这里用了connect by后的ROWNUM, 利用下一个纪录层数是否增加来判断是否叶子。
这必须有一个前提,即CONNECT BY一定是深度优先遍历的,我不知道有没有文档支持。哪天ORACLE改用广度优先,这个逻辑就不成立了。
最后,GROUP BY SUBSTR(ID, 2, 1), SUBSTR(P, 2, 1) 有点小瑕疵,他假定ID和节点符号都只有一位字符。
如果按原料汇总,应该GROUP BY SUBSTR(P, 2, 1)才对,否则当一种原料有多个父亲的话就会有多行出现。

他的反向遍历树最后按叶子GROUP BY还是很巧妙的。

使用道具 举报

回复
论坛徽章:
33
劳斯莱斯
日期:2013-08-08 14:01:23三菱
日期:2013-09-28 10:16:06一汽
日期:2013-11-19 17:01:11凯迪拉克
日期:2013-12-07 17:11:282014年新春福章
日期:2014-02-18 16:42:02马上有房
日期:2014-02-18 16:42:02itpub13周年纪念徽章
日期:2014-09-27 14:20:21itpub13周年纪念徽章
日期:2014-10-08 15:13:38懒羊羊
日期:2015-03-04 14:52:112015年新春福章
日期:2015-03-06 11:58:18
39#
发表于 2008-7-15 10:48 | 只看该作者
原帖由 newkid 于 2008-7-15 10:19 发表
学习一下杨大侠的SQL: 最后的9i版本

SELECT SUBSTR(MAX(D), LENGTH(MAX(D))) C,
SUBSTR(P, 2, 1),
POWER(10, SUM(LOG(10, Q))) Q
FROM
(
SELECT SYS_CONNECT_BY_PATH(ID, '/') ID,
  SYS_CONNECT_BY_PATH(CHILD, '/') P,
  SYS_CONNECT_BY_PATH(PARENT, '/') D,
  CHILD_QTY/PARENT_QTY Q
FROM T_LEVEL1
START WITH ID IN
(
  SELECT ID FROM
  (
   SELECT ID,
    CASE WHEN LEAD(LEVELS) OVER(ORDER BY RN) > LEVELS THEN 0 ELSE 1 END LEAF
   FROM
   (
    SELECT ROWNUM RN, ID, LEVELS, PARENT, CHILD
    FROM T_LEVEL1
    START WITH LEVELS = 1
    CONNECT BY PRIOR CHILD = PARENT
    ORDER SIBLINGS BY LEVELS
   )
  )
  WHERE LEAF = 1
)
CONNECT BY PRIOR PARENT = CHILD
)
GROUP BY SUBSTR(ID, 2, 1), SUBSTR(P, 2, 1) ;

他这里用了connect by后的ROWNUM, 利用下一个纪录层数是否增加来判断是否叶子。
这必须有一个前提,即CONNECT BY一定是深度优先遍历的,我不知道有没有文档支持。哪天ORACLE改用广度优先,这个逻辑就不成立了。
最后,GROUP BY SUBSTR(ID, 2, 1), SUBSTR(P, 2, 1) 有点小瑕疵,他假定ID和节点符号都只有一位字符。
如果按原料汇总,应该GROUP BY SUBSTR(P, 2, 1)才对,否则当一种原料有多个父亲的话就会有多行出现。

他的反向遍历树最后按叶子GROUP BY还是很巧妙的。

<最后,GROUP BY SUBSTR(ID, 2, 1), SUBSTR(P, 2, 1) 有点小瑕疵,他假定ID和节点符号都只有一位字符。
如果按原料汇总,应该GROUP BY SUBSTR(P, 2, 1)才对,否则当一种原料有多个父亲的话就会有多行出现。>
这点还没有测试过,有时间再测试下.

使用道具 举报

回复
论坛徽章:
3
ITPUB9周年纪念徽章
日期:2010-10-08 09:32:252013年新春福章
日期:2013-02-25 14:51:24优秀写手
日期:2014-11-29 06:00:13
40#
发表于 2008-7-15 11:10 | 只看该作者
我测试了一下,发现了问题。如果遇到公用品的话,就会出现问题:

例如下面的案例:

insert into bom values('D','E',1,4)
insert into bom values('A','D',1,4)
insert into bom values('B','D',1,4)

在计算一个A用多少个D的时候,计算结果就不正确

我修改了一下,红色部分是我追加的:
SELECT CHILD ,(SELECT POWER(10,SUM(LOG(10,C_QTY)))/POWER(10,SUM(LOG(10,P_QTY)))
                  FROM BOM where (parent,child) in (
                    select parent,child from bom start with parent = 'A' connect by prior child = parent
                  )
               
                 CONNECT BY PRIOR PARENT=CHILD
                 START WITH CHILD = inner.CHILD
                 --parent = inner.parent  --如果同一个母品目下挂相同的字品目时会出现问题 比较极端的情况,但是也不能排除用户这样干。
                 and rowidtochar(rowid) = inner.frowid --这个地方可以解决在同一个母品目下挂下同的字品目的问题。   
                ) AS C_QTY
        FROM (SELECT b.*,rowidtochar(rowid) frowid
                FROM BOM b
               WHERE NOT EXISTS (SELECT 1 FROM BOM WHERE parent = b.child)
               START WITH PARENT in ('A')
                CONNECT BY PRIOR CHILD = PARENT
             ) INNER
            
接上面的例子,如果用一个语句同时计算A和B各需要多少个E的话,我还是没有想到方法。NewKid,有什么办法吗?


原帖由 newkid 于 2008-7-14 10:41 发表
稍微改了一下,你帮我验证吧:

SELECT CHILD
      ,SUM(C_QTY) AS C_QTY       -- 解决叶子(原料)不唯一
  FROM (SELECT CHILD
              ,(SELECT POWER(10,SUM(LOG(10,C_QTY)))/POWER(10,SUM(LOG(10,P_QTY)))
                  FROM BOM
               CONNECT BY PRIOR PARENT=CHILD
                 START WITH CHILD = inner.CHILD     -- 从每个叶子开始
                            AND PARENT = inner.PARENT    -- 解决叶子(原料)不唯一
                ) AS C_QTY
        FROM (SELECT b.*
                FROM BOM b
               WHERE NOT EXISTS (SELECT 1 FROM BOM WHERE parent = b.child) --解决 9i 没有CONNECT_BY_ISLEAF
                CONNECT BY PRIOR CHILD = PARENT
               START WITH PARENT='A'
             ) inner
      )
GROUP BY CHILD;

[ 本帖最后由 lypch 于 2008-7-15 12:22 编辑 ]

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表