如何根据组数量拆分产品数量

时间:2019-08-11 04:29:38

标签: sql sql-server

我有一张表格,其中列出了具有数量和类别的产品。我想根据产品组数量将它们平均分配。每个组可能包含一个或多个产品。

下表显示了产品及其分组数量

SortOrder   ProductID   ToolGroup   ToolGroupQty    Quantity
1           PRD1            A1         180          900
2           PRD2            A2         77           125
3           PRD3            A2         77           125
4           PRD4            A2         77           135
5           PRD5            A3         128          125
6           PRD6            A3         128          520
7           PRD7            A4         77           385

我尝试的代码是

declare @CombinationGroupTable table(SortOrder int,ProductID nvarchar(50),Combination   nvarchar(20),Tools  int,ToolGroup nvarchar(10),ToolGroupQty int,Market nvarchar(20),Quantity int,isUpdated char(10))

insert into @CombinationGroupTable values(1,'PRD1','A',7,'A1',180,'M0002',900,NULL)
insert into @CombinationGroupTable values(2,'PRD2','A',3,'A2',77,'M0003',125,NULL)
insert into @CombinationGroupTable values(3,'PRD3','A',3,'A2',77,'M0004',125,NULL)
insert into @CombinationGroupTable values(4,'PRD4','A',3,'A2',77,'M0004',135,NULL)
insert into @CombinationGroupTable values(5,'PRD5','A',5,'A3',128,'M0001',125,NULL)
insert into @CombinationGroupTable values(6,'PRD6','A',5,'A3',128,'M0003',520,NULL)
insert into @CombinationGroupTable values(7,'PRD7','A',3,'A4',77,'M0004',385, NULL)

select * from @CombinationGroupTable
declare @SortOrder int,@productID nvarchar(100),@Quantity int,@shift char(1),@prevQty int,@productCode nvarchar(100)
declare @Combination nvarchar(20),@Market nvarchar(50),@Tools int, @prevTools int,@prevComb nvarchar(10), @ToolGroupName nvarchar(20),@tGroupCount int
declare @MaxgroupID nvarchar(20),@NextGroup nvarchar(20), @MaxComb int,@LastSortOrder int,@toCompensate int,@ToolGroup nvarchar(20), @ToolGroupQty int
declare @minOrder int , @maxOrder int, @combProdID nvarchar(100), @combMarket nvarchar(20), @combQty int, @shiftFact int,@combTools int,@combToolsGroup nvarchar(10), @ToolQty int, @toolshiftQty int,@combOrder int, @CToolGroup nvarchar(20)
declare @shiftQty int = 464,@ToolsCount int = 18
declare  @ProdQty table(ID int identity(1,1),SortOrder int,ProductID nvarchar(100),Quantity int,Market nvarchar(10),GroupNo int,ToolGroup nvarchar(20))
declare @RID int,@SOrder int,@CCombination nvarchar(20), @CTotal int, @CompensationQty int,@LastQty int,@RemaininQty int,@PreviousQty int,@ctoolgroupQty int, @tgCompensate int
declare @toolGroupTable table(ToolGroup nvarchar(10),GroupQuantity int,ActQuantity int)

                declare planSchedule cursor for select SortOrder,ProductID,Combination,Tools,ToolGroup,ToolGroupQty,Market,Quantity from @CombinationGroupTable order by SortOrder
                open planSchedule
                fetch next from planSchedule into @sortOrder,@ProductID,@Combination,@Tools,@ToolGroup,@ToolGroupQty,@Market,@Quantity
                while @@FETCH_STATUS=0
                begin

                select  top 1 @MaxComb = isnull(GroupNo,1) from @ProdQty group by GroupNo Order by CAST(GroupNo as int) desc
                set @NextGroup= case when isnull(@LastQty,0) < @shiftQty then isnull(@MaxComb,1) else @MaxComb+1 end

                select @minOrder= MIN(SortOrder),@maxOrder = MAX(SortOrder) from @CombinationGroupTable

                    while @minOrder <= @maxOrder
                    begin

                        select @combMarket= Market,@combQty = Quantity,@combProdID = ProductID,@combTools= Tools,@combToolsGroup= toolGroup,@ctoolgroupQty= ToolGroupQty from @CombinationGroupTable where Combination = @Combination and SortOrder= @minOrder and tools is not null

                        select @ToolQty = cast((3600/62)*(cast(@combTools as numeric)/cast(@ToolsCount as numeric))*8 as int)

                        if(isnull(@Tools,'') <> '' and isnull(@combTools,'') <> '')
                        begin

                                if(isnull(@combQty,0) > @ToolQty)
                                    begin

                                        if((select isnull(sum(quantity),0) from @ProdQty where ToolGroup = @combToolsGroup and GroupNo = @NextGroup) < @ctoolgroupQty)
                                        begin                                                                                           

                                                insert into @ProdQty values(@minOrder,@combProdID,@ctoolgroupQty,@combMarket,@NextGroup,@combToolsGroup)
                                                insert into @toolGroupTable values(@combToolsGroup,@ctoolgroupQty,@ctoolgroupQty)

                                                update @CombinationGroupTable set Quantity= Quantity - @ctoolgroupQty,ToolGroupQty= @ctoolgroupQty,isUpdated='Y' where productID= @combProdID --and ToolGroup = @combToolsGroup                                             
                                        end

                                    end 

                                    else
                                    begin

                                        insert into @ProdQty values(@minOrder,@combProdID,@combQty,@combMarket,@NextGroup,@combToolsGroup)  

                                        insert into @toolGroupTable values(@combToolsGroup,@combQty,@ctoolgroupQty)                                                                     

                                        update @CombinationGroupTable set  Tools = @Tools,Quantity=Quantity-@combQty  where  ProductID = @combProdID --ToolGroup= @ToolGroup and isnull(isUpdated,'N')='N' and SortOrder= @minOrder + 1 and ToolGroup= @combToolsGroup                                  

                                        set @combQty = 0 
                                    end


                        if not exists(select * from @CombinationGroupTable where ProductID = @combProdID and isupdated='Y')
                        update @CombinationGroupTable set Quantity = case when @combQty >= @ToolQty then (Quantity-@ToolQty) else (Quantity-@combQty) end,isUpdated='Y'  where ProductID = @combProdID

                        delete from @CombinationGroupTable where Quantity <= 0


                        end


                        if exists(select * from (select sum(GroupQuantity) Qty,sum(ActQuantity) ActQuantity,ToolGroup from @toolGroupTable group by ToolGroup)A where Qty < ActQuantity)
                        begin
                            set @tgCompensate = 0 

                            select @tgCompensate=ActQuantity-Qty from (
                            select sum(GroupQuantity) Qty,sum(ActQuantity) ActQuantity,ToolGroup from @toolGroupTable group by ToolGroup)A
                            where Qty < ActQuantity

                            select @combMarket= Market,@combQty = Quantity,@combProdID = ProductID,@combTools= Tools,@combToolsGroup= toolGroup,@ctoolgroupQty= ToolGroupQty from @CombinationGroupTable where SortOrder= @minOrder+1  and ToolGroup= @combToolsGroup

                            insert into @ProdQty values(@minOrder,@combProdID,@tgCompensate,@combMarket,@NextGroup,@combToolsGroup)
                            insert into @toolGroupTable values(@combToolsGroup,@tgCompensate,@ctoolgroupQty)

                            update @CombinationGroupTable set Quantity= Quantity - @tgCompensate,Tools=@Tools ,ToolGroupQty= @ToolQty where productID= @combProdID and ToolGroup = @combToolsGroup                          

                            delete from @CombinationGroupTable where Quantity <=0

                            set @tgCompensate = 0 

                            delete from @toolGroupTable



                        end


                        delete from @toolGroupTable
                        delete from @CombinationGroupTable where Quantity <= 0                  

                        set @minOrder= @minOrder+1
                        set @combMarket= '' set @combQty = 0 set @combProdID = '' set @combTools = 0


                    end

                    set @LastQty = 500000

                    delete from @CombinationGroupTable where Quantity <=0

                fetch next from planSchedule into @sortOrder,@ProductID,@Combination,@Tools,@ToolGroup,@ToolGroupQty,@Market,@Quantity
                end

                close planSchedule
                deallocate planSchedule


                select * from @ProdQty




实际结果应该如下

SortOrder   ProductID   ToolGroup   Quantity    SplitedGroup
1           PRD1           A1       180          1
2           PRD2           A2       77           1
5           PRD5           A3       125          1
6           PRD6           A3       3            1
7           PRD7           A4       77           1
1           PRD1           A1       180          2
2           PRD2           A2       48           2
3           PRD3           A2       29           2
6           PRD6           A3       129          2
7           PRD7           A4       77           2
1           PRD1           A1       180          3
3           PRD3           A2       77           3
6           PRD6           A3       129          3
7           PRD7           A4       77           3
1           PRD1           A1       180          4
3           PRD3           A2       19           4
4           PRD4           A2       58           4
6           PRD6           A3       129          4
7           PRD7           A4       77           4
1           PRD1           A1       180          5
4           PRD4           A2       77           5
6           PRD6           A3       129          5
7           PRD7           A4       77           5

Excel格式的预期结果 enter image description here

3 个答案:

答案 0 :(得分:1)

尝试以下脚本。我最多考虑了10个SplitedGroup,并在IUNION 1到10之间创建了一个内联表“ B”。但是,如果有更多SplitedGroup的可能性,则可以增加该范围。

您可以选中DEMO HERE

SELECT *,
ROW_NUMBER() OVER(PARTITION BY SortOrder ORDER BY SortOrder ASC,ToolGroupQty DESC ) RN
FROM
(
    SELECT SortOrder, ProductID,ToolGroup,ToolGroupQty
    FROM
    (
        SELECT SortOrder, ProductID,ToolGroup,ToolGroupQty,
        Quantity/ToolGroupQty N
        FROM your_table
    )A
    INNER JOIN (
        --Here you can add more values to increase the Range
        SELECT 1 N UNION ALL SELECT 2 N UNION ALL SELECT 3 N UNION ALL SELECT 4 N UNION ALL SELECT 5 N UNION ALL
        SELECT 6 N UNION ALL SELECT 7 N UNION ALL SELECT 8 N UNION ALL SELECT 9 N UNION ALL SELECT 10 N
    ) B ON A.N >= B.N

    UNION ALL

    SELECT SortOrder, ProductID,ToolGroup,
    Quantity%ToolGroupQty ToolGroupQty 
    FROM your_table
    WHERE Quantity%ToolGroupQty > 0
)C
  

注意:我想您在示例输出中对SortOrder = 3的分配有误。结果,您有23个,但我的查询在输出中得到22行。

答案 1 :(得分:1)

每种产品将通过以下三种方式之一进行拆分。它将填充已经部分填充(但可能不是完全填充)的桶的末尾,完全填充一个桶或部分填充一个新的空桶。这里的想法是确定这些边界落在哪里(步骤1和2),然后根据这些参数(通过三部分并集)生成所需的输出。

with step1 as (
    select *,
        (
          sum(Quantity)
              over (partition by ToolGroup order by SortOrder)
              - Quantity
        ) / ToolGroupQty + 1 as FirstSplitGroup,
        (
          sum(Quantity)
              over (partition by ToolGroup order by SortOrder)
              - Quantity
        ) % ToolGroupQty as GMod
    from data
), step2 as (
    select *,
        FirstSplitGroup as PartialSplitGroup1,
        case when PartialSplitQty1 > 0 then 1 else 0 end as Adj,
        (Quantity - PartialSplitQty1) / ToolGroupQty as FullSplitCnt,
        case when Quantity > ToolGroupQty - GMod
            then (Quantity + GMod) % ToolGroupQty
            else 0
         end as PartialSplitQty2        
    from step1 cross apply (select 
            case when Quantity < ToolGroupQty - GMod
                then Quantity
                else (ToolGroupQty - GMod) % ToolGroupQty
            end PartialSplitQty1
        ) as psq1
), num as (
    select 0 n union all select 1 union all select 2 union all
    select 3 union all select 4 union all select 5 union all
    select 6 union all select 7 union all select 8 union all
    select 9
)
select 1, 
    ToolGroup,
    PartialSplitGroup1 as SplitGroup,
    ProductId,
    PartialSplitQty1 as SplitQty
from step2
where PartialSplitQty1 > 0
union all
select 2,
    ToolGroup,
    PartialSplitGroup1 + Adj + FullSplitCnt,
    ProductId,
    PartialSplitQty2
from step2
where PartialSplitQty2 > 0
union all
select 3,
    ToolGroup,
    FirstSplitGroup + Adj + n,
    ProductId,
    ToolGroupQty
from step2 inner join num
    on n < FullSplitCnt
order by ToolGroup, SplitGroup, ProductId, SplitQty;

https://rextester.com/BBJ90851

答案 2 :(得分:0)

我认为解决这个问题的唯一合理方法是将产品分解为1个数量,然后重新组合。可以对较大的块执行以下方法,但是从单个产品的角度考虑对我有帮助。

因此,您可以使用递归CTE分解产品。然后,您需要将它们结合起来。

乍一看,这很容易。只需枚举它们并将它们划分为存储桶-这是使用窗口函数的简单计算。

以下内容采用了更为复杂的方法:

with cte as (
      select sortOrder, productid, toolgroup, 1 as qty, (quantity - 1) as qty_left, toolgroupqty, quantity as orig_quantity, 1 as lev
      from data
      union all
      select sortOrder, productid, toolgroup,
             1 as qty,
             (qty_left - 1) as qty_left,
             toolgroupqty, orig_quantity, lev + 1
      from cte
      where qty_left > 0  and lev < 1000
     ),
     cte2 as (
      select cte.*,
             (row_number() over (order by orig_quantity / toolgroupqty, sortorder, newid()) - 1) * 5 / count(*) over () as bucket
      from cte
     )
select sortorder, productid, toolgroup, count(*) as qty, bucket
from cte2
group by sortorder, productid, toolgroup, bucket
order by bucket, sortorder
option (maxrecursion 0);

Here是db <>小提琴。

似乎还有其他限制:

  • “拆分”(或存储桶)中的金额永远不会超过toolquantity
  • 但是,有剩菜剩饭,因此可以处理的金额更少。

partition by中的cte2根据数量除以刀具数量对值进行分块。这有助于确保每个存储桶中只有一个块。当然,没有完美的保证,因为一种产品可能会主导投入。

顺便说一句,“ 5”是存储桶数。从这个问题尚不清楚,您如何确定这一点。

顺便说一句,如果这对您有用,但是您希望提高性能,那么如果您提出一个 new 问题,我将不胜感激。