使用MS-SQL和/或C#比较物料清单(BOM)版本

时间:2016-08-04 21:59:53

标签: c# asp.net sql-server

所有

我使用带有Microsoft SQL 2012数据库的C#应用​​程序创建ASP.Net,其中一个功能需要是BOM比较。从我们的PDM系统数据库中提取BOM信息。我可以使用递归查询来提取两个单独的BOM,但我不确定如何进行实际比较。

比较需要检查数量是否已更改,零件是否已完全移除,或者是否添加了新零件。我用一系列合并语句对此进行了测试,但我遇到的问题是,如果一个部分具有相同的父级和相同的级别,但它的父级是不同的。

示例 - 部件号1003在两个地方处于同一级别(从级别0开始),即使它的父级不同。

  1. 1000

    • 1001
    • 1002

      • 1003
        • 1004
        • 1005
    • 1006

      • 1003
        • 1004
        • 1005
  2. Results from the two BOM's

    结果的结构是ObjectId是部件的标识符,ParentObjectId是部件的父标识符,TopObjectId是部件顶部的标识符。数量是装配该级别的部件号的编号。 TreeObjectId是父/子关系表的标识符。如果数量发生变化,ObjectId不会改变,但如果部件的修订版本发生变化,它会发生变化。如果程序集的修订版发生更改,则TreeObjectId和TopObjectId会更改。

    我想要的结果基本上会在修改数量时标记旧的装配行,如果添加了不存在的零件则插入一行,如果该行已被删除则标记该行。

    我更喜欢在数据库端执行此操作,但如果有更好的方法在C#端执行此操作,那对我来说没问题。

    以下是运行递归查询的代码:

    CREATE TABLE #LatestBOM (ObjectId int, PartNumber varchar(255), ParentObjectId int, ParentPartNumber varchar(255), TopObjectId int,
        TopPartNumber varchar(255), TreeLevel int, Quantity int, TreeObjectId int, ChangeFlag int);
    
    CREATE TABLE #OrderedBOM (ObjectId int, PartNumber varchar(255), ParentObjectId int, ParentPartNumber varchar(255), TopObjectId int,
        TopPartNumber varchar(255), TreeLevel int, Quantity int, TreeObjectId int, ChangeFlag int);
    
    DECLARE @LatestBOMObjectId int, @OrderedBOMObjectId int;
    SET @LatestBOMObjectId = 149276;
    SET @OrderedBOMObjectId = 149023;
    
    WITH BOM (ObjectId, ParentObjectId, TopObjectId, TreeLevel, Quantity, TreeObjectId, ChangeFlag)
     AS
    (
    -- Anchor member definition
        SELECT TopLevelAssembly.[OBJECT_ID] as ObjectId, TopLevelAssembly.[OBJECT_ID] as ParentObjectId, @LatestBOMObjectId as TopObjectId,
            0 as TreeLevel, CAST(1.00 AS FLOAT) as Quantity, null as TreeObjectId, 1 as ChangeFlag
        FROM SMARTEAM.dbo.TN_DOCUMENTS as TopLevelAssembly
        WHERE TopLevelAssembly.[OBJECT_ID] = @LatestBOMObjectId
        UNION ALL
        SELECT Children.SON_OBJECT_ID as ObjectId, Children.OBJECT_ID1 as ParentObjectId, @LatestBOMObjectId as TopObjectId,
            BL.TreeLevel + 1, CN_QUANTITY as Quantity, Children.[OBJECT_ID] as TreeObjectId, 1 as ChangeFlag
        FROM SMARTEAM.dbo.DOCUME0_TREE as Children
        INNER JOIN BOM as BL
        on Children.OBJECT_ID1 = BL.ObjectId
        where Children.OBJECT_ID1 IS NOT NULL
    )
    
    INSERT INTO #LatestBOM (ObjectId, PartNumber, ParentObjectId, ParentPartNumber, TopObjectId, TopPartNumber, TreeLevel, Quantity, TreeObjectId, ChangeFlag)
    select ObjectId, PartProduct.CN_PART_NUMBER as PartNumber, ParentObjectId, ParentPartProduct.CN_PART_NUMBER as ParentPartNumber, TopObjectId,
        TopPartProduct.CN_PART_NUMBER as TopPartNumber, TreeLevel, Quantity, TreeObjectId, ChangeFlag
    from BOM
    left join SMARTEAM.dbo.TN_DOCUMENTS PartProduct
    on BOM.ObjectId = PartProduct.[OBJECT_ID]
    left join SMARTEAM.dbo.TN_DOCUMENTS ParentPartProduct
    on BOM.ParentObjectId = ParentPartProduct.[OBJECT_ID]
    left join SMARTEAM.dbo.TN_DOCUMENTS TopPartProduct
    on BOM.TopObjectId = TopPartProduct.[OBJECT_ID];
    
    WITH BOM (ObjectId, ParentObjectId, TopObjectId, TreeLevel, Quantity, TreeObjectId, ChangeFlag)
     AS
    (
    -- Anchor member definition
        SELECT TopLevelAssembly.[OBJECT_ID] as ObjectId, TopLevelAssembly.[OBJECT_ID] as ParentObjectId, @OrderedBOMObjectId as TopObjectId,
            0 as TreeLevel, CAST(1.00 AS FLOAT) as Quantity, null as TreeObjectId, 1 as ChangeFlag
        FROM SMARTEAM.dbo.TN_DOCUMENTS as TopLevelAssembly
        WHERE TopLevelAssembly.[OBJECT_ID] = @OrderedBOMObjectId
        UNION ALL
        SELECT Children.SON_OBJECT_ID as ObjectId, Children.OBJECT_ID1 as ParentObjectId, @OrderedBOMObjectId as TopObjectId,
            BL.TreeLevel + 1, CN_QUANTITY as Quantity, Children.[OBJECT_ID] as TreeObjectId, 1 as ChangeFlag
        FROM SMARTEAM.dbo.DOCUME0_TREE as Children
        INNER JOIN BOM as BL
        on Children.OBJECT_ID1 = BL.ObjectId
        where Children.OBJECT_ID1 IS NOT NULL
    )
    
    INSERT INTO #OrderedBOM (ObjectId, PartNumber, ParentObjectId, ParentPartNumber, TopObjectId, TopPartNumber, TreeLevel, Quantity, TreeObjectId, ChangeFlag)
    select ObjectId, PartProduct.CN_PART_NUMBER as PartNumber, ParentObjectId, ParentPartProduct.CN_PART_NUMBER as ParentPartNumber, TopObjectId,
        TopPartProduct.CN_PART_NUMBER as TopPartNumber, TreeLevel, Quantity, TreeObjectId, ChangeFlag
    from BOM
    left join SMARTEAM.dbo.TN_DOCUMENTS PartProduct
    on BOM.ObjectId = PartProduct.[OBJECT_ID]
    left join SMARTEAM.dbo.TN_DOCUMENTS ParentPartProduct
    on BOM.ParentObjectId = ParentPartProduct.[OBJECT_ID]
    left join SMARTEAM.dbo.TN_DOCUMENTS TopPartProduct
    on BOM.TopObjectId = TopPartProduct.[OBJECT_ID];
    
    
    select * from #LatestBOM;
    select * from #OrderedBOM;
    drop table #LatestBOM;
    drop table #OrderedBOM;
    

    以下是我尝试查找差异的代码,但在不同的更高级别的父级下遇到了相同级别的问题:

    --checks if a part is in the Order that isn't in the BOM (removed from BOM), if it is then add it to the #tempbomhistory with a changeflag of 2
    WITH CTE AS (select OrderDetailId, OrderId, NeedQtyOld, NeedQtyNew, OrderQtyOld, OrderQtyNew, IsDeletedOld, IsDeletedNew, ChangeFlag, ObjectID, ParentProductObjectId, TopLevelProductObjectId, TreeLevel,
        MakeBuy
        from #TempBOMWithHistory)
    MERGE CTE AS TARGET
        USING ( 
        Select OrderDetailId, OrderId, NeedQty, ObjectId, ParentProductObjectId, TopLevelProductObjectId, TreeLevel, MakeBuy
        from Order_Detail
        left JOIN SMARTEAM.dbo.TN_DOCUMENTS TopLevelPartNumber ON Order_Detail.TopLevelProductObjectId = TopLevelPartNumber.[OBJECT_ID]
        where TopLevelPartNumber.CN_PART_NUMBER = @TopLevelPartNumber AND OrderId = @OrderId)
         AS SOURCE
            ON (TARGET.OrderDetailId = SOURCE.OrderDetailId)
            WHEN NOT MATCHED THEN
                INSERT (OrderDetailId, OrderId, NeedQtyOld, NeedQtyNew, OrderQtyOld, OrderQtyNew, IsDeletedOld, IsDeletedNew,
                    ChangeFlag, ObjectId, ParentProductObjectId, TopLevelProductObjectId, TreeLevel, MakeBuy)
                VALUES (SOURCE.OrderDetailId, SOURCE.OrderId, SOURCE.NeedQty, SOURCE.NeedQty, SOURCE.NeedQty, SOURCE.NeedQty, 0, 1,
                    2, SOURCE.ObjectId, SOURCE.ParentProductObjectId, SOURCE.TopLevelProductObjectId, SOURCE.TreeLevel, SOURCE.MakeBuy);
    
    --checks if a new part is in the BOM that isn't in the Order (new part in BOM), if it isn't in the order then set the changeflag in #tempBomWithHistory to 3
    WITH CTE AS (select OrderDetailId, OrderId,NeedQtyOld, NeedQtyNew, OrderQtyOld, OrderQtyNew, IsDeletedOld, IsDeletedNew,
        ChangeFlag, ObjectId, ParentProductObjectId, TopLevelProductObjectId, TreeLevel, MakeBuy, ParentPartNumber 
        from #TempBOMWithHistory)
    MERGE CTE AS TARGET
        USING (Select OrderDetailId, OrderId, NeedQty, OrderQty, LastModifiedTime, LastModifiedBy, ChangeFlag,
        ObjectId, ParentProductObjectId, TopLevelProductObjectId, TreeLevel, MakeBuy
        from Order_Detail
        WHERE OrderId = @OrderId) AS SOURCE
            ON (TARGET.OrderDetailId = SOURCE.OrderDetailId)
            WHEN NOT MATCHED BY SOURCE THEN
                UPDATE SET
                    ChangeFlag = 3, NeedQtyOld = TARGET.NeedQtyNew, OrderQtyOld = TARGET.NeedQtyNew, OrderQtyNew = TARGET.NeedQtyNew, IsDeletedOld = 0, IsDeletedNew = 0;
    
    --checks the part is in the Order and BOM but a attribute has changed, ignore anything that already has a changeflag pending (should only possibly be changeflag 2)
    WITH CTE AS (Select * from #TempBOMWithHistory Where ChangeFlag = 0)
    MERGE CTE AS TARGET
    USING (select 
        OrderDetailId, NeedQty, OrderQty, MakeBuy
        from Order_Detail
        WHERE OrderId = @OrderId
        ) AS SOURCE
    ON (TARGET.OrderDetailId = SOURCE.OrderDetailId)
    WHEN MATCHED AND TARGET.NeedQtyNew = SOURCE.NeedQty
    THEN
    delete
    WHEN MATCHED    
    THEN
    UPDATE SET
        TARGET.NeedQtyOld = SOURCE.NeedQty, TARGET.OrderQtyOld = SOURCE.OrderQty,TARGET.OrderQtyNew = SOURCE.OrderQty,
        TARGET.IsDeletedOld = 0, TARGET.IsDeletedNew = 0, TARGET.MakeBuy = SOURCE.MakeBuy, TARGET.ChangeFlag = 1;
    

    谢谢!

2 个答案:

答案 0 :(得分:0)

嗯, fuhgeddabout "在数据库方面这样做的概念!"在这种情况下,SQL可以为您做的就是提取信息,您需要通过执行系列查询来完成这些信息。 (不要计划使用"递归查询。"您需要一次发出一次。)

该过程的第一步只是提取所有(嵌套)列表。每当你遇到对#34的引用时,你还没有找到另一件事,"你需要记住你需要检索它。这个步骤一直持续到你最终检索到所有内容:你的"待办事项列表"现在终于空了。

下一步将是"了解每个BOM。"循环遍历结构,查找项目编号的每个实例,无论它出现在哪里,依此类推。

然后可能需要许多特定于应用程序的逻辑来协调令人讨厌的事情,例如"部件的修订。" 此时,您已经深入了解& #34; 业务的视角,以及它为生活所做的事情。'"

同样,最终("来自业务部门的观点")步骤"比较两个BOM以了解它们如何不同"将是一个高度针对特定业务的决策的混乱。

我真正建议你的是,你应该留意任何抽象的机会"和"概括"这实际上使业务 -sense。尝试避免最小化(ahem)同样决定的情况数"是在很多不连贯的地方制作的#34;在整个源代码中,所以 if 当你正在调试这个东西,或者做一些工程师提出的下一个增强时,你可以快速归零最少的"要观察或改变的地方。"所以, as 你随着时间的推移做出这个和未来的变化,各种逻辑仍然是" un-coupled"尽可能。

你有......你的工作适合你。

答案 1 :(得分:0)

好吧我觉得我现在有这个工作。在我生成BOM的递归查询中,我添加了一个名为hierarchy的附加列,它打印出每个部分的树路径的部件号。这将唯一标识每个部分及其在树中的位置。初步测试似乎工作得很好......

从这里借用一些sql代码来修改我的查询:http://www.codeproject.com/Articles/818694/SQL-queries-to-manage-hierarchical-or-parent-child

WITH BOM (ObjectId, ParentObjectId, TopObjectId, TreeLevel, Hierarchy, Quantity, TreeObjectId, ChangeFlag)
 AS
(
-- Anchor member definition
    SELECT TopLevelAssembly.[OBJECT_ID] as ObjectId, TopLevelAssembly.[OBJECT_ID] as ParentObjectId, @TopLevelObjectId as TopObjectId,
        0 as TreeLevel, CAST(TopLevelAssembly.CN_PART_NUMBER as varchar(MAX)) as Hierarchy, CAST(1.00 AS FLOAT) as Quantity, null as TreeObjectId, 1 as ChangeFlag
    FROM SMARTEAM.dbo.TN_DOCUMENTS as TopLevelAssembly
    WHERE TopLevelAssembly.[OBJECT_ID] = @TopLevelObjectId
    UNION ALL
    SELECT Children.SON_OBJECT_ID as ObjectId, Children.OBJECT_ID1 as ParentObjectId, @TopLevelObjectId as TopObjectId,
        BL.TreeLevel + 1, CAST(CASE WHEN CAST(BL.ParentObjectId as varchar(max)) = ''
        THEN(CAST(ChildrenHierarchy.CN_PART_NUMBER AS VARCHAR(MAX)))
        ELSE(CAST(bl.Hierarchy as Varchar(max)) + '.' + CAST(ParentHierarchy.CN_PART_NUMBER AS VARCHAR(MAX)))
    END AS VARCHAR(MAX)) as Hierarchy, (BL.Quantity * CN_QUANTITY) as Quantity, Children.[OBJECT_ID] as TreeObjectId, 1 as ChangeFlag
    FROM SMARTEAM.dbo.DOCUME0_TREE as Children
    INNER JOIN BOM as BL
    on Children.OBJECT_ID1 = BL.ObjectId
    INNER join SMARTEAM.dbo.TN_DOCUMENTS ChildrenHierarchy
    on BL.ObjectId = ChildrenHierarchy.[OBJECT_ID]
    INNER join SMARTEAM.dbo.TN_DOCUMENTS ParentHierarchy
    on BL.ParentObjectId = ParentHierarchy.[OBJECT_ID]
    where Children.OBJECT_ID1 IS NOT NULL
)