CTE乘法导致多个结果行

时间:2018-07-24 18:35:31

标签: sql sql-server tsql

编辑-问题是同一项目在BOM表的不同位置多次显示,然后为每个实例而不是一个实例生成一个结果行。此问题已解决。谢谢

我有以下代码

WITH tBomCTE (ParentItem, ChildItem, WorkCentre, Operation, Quantity, ActualQuantity, ParentUnitWeight, ParentWeightUnitOfMeasure, ChildUnitWeight, ChildWeightUnitOfMeasure, BomLevel, MaterialClass, ParentItemSource) AS
(
    SELECT
        id.parentitem, id.ChildItem, id.WorkCentre, id.Operation, 
        id.Quantity, id.Quantity, id.ParentUnitWeight, 
        id.ParentWeightUnitOfMeasure, id.ChildUnitWeight, 
        id.ChildWeightUnitOfMeasure, 
        0 as BomLevel, id.MaterialClassCode, ParentItemSource
    FROM
        @tItemDenomalized id
    WHERE
        id.parentitem = '10054471'

    UNION ALL

    SELECT
        id.parentitem, id.ChildItem, id.WorkCentre, id.Operation, 
        id.Quantity, CAST((id.Quantity * b.ActualQuantity) AS DECIMAL(19,8)), id.ParentUnitWeight, 
        id.ParentWeightUnitOfMeasure, id.ChildUnitWeight, 
        id.ChildWeightUnitOfMeasure, 
        BomLevel + 1, 
        id.MaterialClassCode, id.ParentItemSource
    FROM
        tBomCTE b
    JOIN
        @tItemDenomalized id ON b.ChildItem = id.parentitem
)
SELECT DISTINCT
    'T1', ParentItem, ChildItem, WorkCentre, Operation, Quantity,  
    ActualQuantity, 
    COALESCE(ParentUnitWeight, 0), ParentWeightUnitOfMeasure, 
    COALESCE(ChildUnitWeight, 0), ChildWeightUnitOfMeasure, BomLevel, 
    MaterialClass, ParentItemSource
FROM 
    tBomCTE

问题在于此代码产生了多个结果行。我已将其隔离到cast((id.Quantity * b.ActualQuantity) as decimal(19,8))行。

基本上,我正在尝试构建材料明细表(BOM),但我们遇到了数量未正确加总的问题。例如,如果我们需要2个父项目,则子项目数量仅反映我们对1个项目的需求。

因此添加了这一行。它从来没有引起问题,但是我们只是进行了测试,现在正在引起问题。

特别是。我有一个父项,然后是子项1,然后是子项2。运行此代码时,我得到了3个关于子项2的结果,这些结果都具有与父项相同的路径。所以那没有道理。然后3个子2项目的数量分别是22、44、66。

如果我不得不猜测看起来发生了什么,那就是数量确实乘以父代。然后变成22。然后我乘以下一个父级,而不是乘以,而是完全创建一个新行。

现在,我的解决方案是使用代码更新数量,然后删除所有重复的行以消除多余的行。但这是不好的做法。

为什么会产生多行而不是将父项乘以当前项?

编辑。

这是导致问题的整个存储过程:

IF EXISTS (SELECT * 
       FROM   sysobjects 
       WHERE  id = object_id(N'[dbo].[spSAL_BomRecursive]') 
              AND OBJECTPROPERTY(id, N'IsProcedure') = 1 )
BEGIN
    DROP PROCEDURE [dbo].[spSAL_BomRecursive];
END
GO

CREATE PROCEDURE [dbo].[spSAL_BomRecursive]
(
     @SessionId             varchar(50)
    ,@Item                  [dbo].[ItemType] = NULL
    ,@DebugLevel            BIT = 0
    ,@CurrentOrStandardBOM  nvarchar(1) = 'C'
)
AS
BEGIN

SET NOCOUNT ON;

--declare @Item         varchar(30);
--set @item = '10029554';

Declare @tItemDenomalized TABLE (ParentItem nvarchar(30), 
                                 ChildItem nvarchar(30), 
                                 WorkCentre nvarchar(30) ,
                                 Operation nvarchar(30),  
                                 Quantity decimal(19,8), 
                                 ParentUnitWeight decimal(18,9),
                                 ParentWeightUnitOfMeasure nvarchar(3), 
                                 ChildUnitWeight decimal(18,9), 
                                 ChildWeightUnitOfMeasure nvarchar(3), 
                                 MaterialClassCode nvarchar(30),
                                 ParentItemSource nvarchar(30));

Declare @CurrentOrStandardSuffix int
Set @CurrentOrStandardSuffix = Case when @CurrentOrStandardBOM = 'C' then 0 else 1 end

-- populate a table with all of the items, merging data to make the recursive SQL easier 
Insert into @tItemDenomalized
        select distinct i.item,  coalesce(jm.item, ''), jr.wc, jr.oper_num, coalesce(jm.matl_qty, 0) as qty ,i.unit_weight as ParentUnitWeight, i.weight_units as ParentWeightUnitOfMeasure, i2.unit_weight as ChildUnitWeight, i2.weight_units as ChildWeightUnitOfMeasure, i.Uf_SalMaterialClassCode as MaterialClass, i.p_m_t_code
        from item_mst i
          left join jobroute_mst jr on i.job = jr.job and jr.suffix = @CurrentOrStandardSuffix
          left join jobmatl_mst jm on jr.job = jm.job and jr.oper_num = jm.oper_num and jr.suffix = jm.suffix
          left join item_mst i2 on coalesce(jm.item, '') = i2.item;

WITH tBomCTE ( ParentItem, ChildItem, WorkCentre, Operation, Quantity, ActualQuantity, ParentUnitWeight, ParentWeightUnitOfMeasure, ChildUnitWeight, ChildWeightUnitOfMeasure, BomLevel, MaterialClass, ParentItemSource )
    AS
    (
        select id.parentitem, id.ChildItem, id.WorkCentre, id.Operation, id.Quantity, id.Quantity, id.ParentUnitWeight, id.ParentWeightUnitOfMeasure, id.ChildUnitWeight, id.ChildWeightUnitOfMeasure, 0 as BomLevel, id.MaterialClassCode, ParentItemSource
        from @tItemDenomalized id
          where id.parentitem = @item

    UNION ALL

        select id.parentitem, id.ChildItem, id.WorkCentre, id.Operation, id.Quantity, cast((id.Quantity * b.ActualQuantity) as decimal(19,8)) , id.ParentUnitWeight, id.ParentWeightUnitOfMeasure, id.ChildUnitWeight, id.ChildWeightUnitOfMeasure, BomLevel+1, id.MaterialClassCode, id.ParentItemSource
        from tBomCTE b
          join @tItemDenomalized id on b.ChildItem = id.parentitem
    )


insert into tSAL_Bom
           ([SessionId],[ParentItem],[ChildItem],[WorkCentre],[Operation],[Quantity],[ActualQuantity],[ParentUnitWeight],[ParentWeightUnitOfMeasure],[ChildUnitWeight],[ChildWeightUnitOfMeasure],[BomLevel],[MaterialClassCode],[ParentItemSource])
    SELECT distinct @SessionId, ParentItem, ChildItem, WorkCentre, Operation, Quantity, ActualQuantity, coalesce(ParentUnitWeight, 0), ParentWeightUnitOfMeasure, coalesce(ChildUnitWeight, 0), ChildWeightUnitOfMeasure, BomLevel, MaterialClass, ParentItemSource
    FROM tBomCTE

-- cleanup the table from yesterday
delete from tSAL_Bom
 where CreatedOn < Getdate()-1

RETURN 0;

END
GO

有问题的数据行如下

SortingOrder    DepthLevel  ItemOrWorkCenterNumber  BaseQuantity    Quantity

[10054471]          0             10054471               1             1

[10054471][1605][10008773]  1   10008773    1   2

[10054471][1605][10008773][1100][10024306]  2   10024306    2   4

[10054471][1605][10008773][1100][10024306][1005][10030273]  3   10030273    11  22

[10054471][1605][10008773][1100][10024306][1005][10030273]  3   10030273    11  44

[10054471][1605][10008773][1100][10024306][1005][10030273]  3   10030273    11  66

因此您可以看到。应该有1行看起来像这样

[10054471][1605][10008773][1100][10024306][1005][10030273]  3   10030273    11  88

因为主要的父母需要1。第一个孩子需要2。所以我们的乘数是2。我们的下一个父母通常需要2,但是乘数需要4,这使我们现在的乘数8。这行是正确的。然后,下一行应该是11乘以8的乘数的基数。所以是88。但是我得到的是一行乘以2、4和6的行。

1 个答案:

答案 0 :(得分:1)

这实际上不是您问题的答案,而是试图将大量逻辑分解为可重复的东西。

我写了一个非常快速的自封装查询,“某种”可以执行我认为您要尝试执行的操作。也许您可以做一些类似的事情来解释我的逻辑与您的逻辑之间的区别?

WITH Base AS (
    SELECT 1 AS id, NULL AS parent, 1 AS multiplier
    UNION ALL
    SELECT 2 AS id, 1 AS parent, 2 AS multiplier
    UNION ALL
    SELECT 3 AS id, 2 AS parent, 4 AS multiplier),
Recurs AS (
    SELECT
        id,
        1 AS depth,
        multiplier
    FROM
        Base
    WHERE
        id = 1
    UNION ALL
    SELECT
        b.id,
        depth + 1 AS depth,
        b.multiplier * r.multiplier AS multiplier
    FROM
        Base b
        INNER JOIN Recurs r ON r.id = b.parent),
SecondRecurs AS (
    SELECT
        id,
        depth,
        multiplier
    FROM
        Recurs
    UNION ALL
    SELECT
        p.parent,
        s.depth,
        s.multiplier
    FROM
        SecondRecurs s
        INNER JOIN Base b ON b.id = s.id
        INNER JOIN Base p ON p.id = b.parent),
Ordered AS (
    SELECT
        *,
        ROW_NUMBER() OVER (ORDER BY depth DESC, id) AS order_id
    FROM
        SecondRecurs)
SELECT
    id,
    depth,
    multiplier
FROM
    Ordered
WHERE
    order_id = 1;

那么这是如何工作的?

首先,我进行一些测试数据:

id  parent  multiplier
1   NULL    1
2   1   2
3   2   4

然后,我使用递归CTE来获取深度/乘数,使用与您的示例类似的逻辑:

id  depth   multiplier
1   1   1
2   2   2
3   3   8

但是我故意让它运行而不必担心让孩子们成父母,所以现在我进入了第二阶段,将其整理成某种顺序:

id  depth   multiplier  order_id
1   3   8   1
3   3   8   2
NULL    2   2   3
2   2   2   4
1   1   1   5

最后,我可以选择想要的行,而忽略您似乎在查询中得到的“部分”结果吗?

id  depth   multiplier
1   3   8

这完全有帮助吗?