如何连接2个表来分配项目?

时间:2018-12-12 21:02:29

标签: sql sql-server tsql

我创建了2个具有库存信息(项目,位置,数量)的表。其中NeedInv个项目/项目中需要X个项目。另一个HaveInv的项目/位置的项目数超过X

我正在尝试合并或组合这两个表以输出应在哪些位置之间转移哪些项目。我有针对单个分发位置执行此操作的代码,并且我尝试对其进行修改并添加逻辑以使其可用于多个分发位置,但是在某些情况下仍然失败。

我已经创建了[sqlfiddle] 1,但是示例数据如下:

CREATE TABLE NeedInv
    (item int, location varchar(1), need int)

INSERT INTO NeedInv
    (item, location, need)
VALUES
    (100, 'A', 4), (100, 'B', 0), (100, 'C', 2), (200, 'A', 0), (200, 'B', 1), (200, 'C', 1), (300, 'A', 3), (300, 'B', 5), (300, 'C', 0)

CREATE TABLE HaveInv
    (item int, location varchar(1), have int)

INSERT INTO HaveInv
    (item, location, have)
VALUES
    (100, 'A', 0), (100, 'B', 3), (100, 'C', 0), (100, 'D', 3), (200, 'A', 1), (200, 'B', 0), (200, 'C', 0), (200, 'D', 1), (300, 'A', 0), (300, 'B', 0), (300, 'C', 20), (300, 'D', 5)

CREATE TABLE DesiredOutput
    (item int, SourceLocation varchar(1), TargetLocation varchar(1), Qty int)

INSERT INTO DesiredOutput
    (item, SourceLocation, TargetLocation, Qty)
VALUES
    (100, 'B', 'A', 3), (100, 'D', 'A', 1), (100, 'D', 'C', 2), (200, 'A', 'B', 2), (200, 'A', 'C', 3), (200, 'D', 'C', 1), (300, 'C', 'A', 3), (300, 'C', 'B', 3)

由于连接表,我试图输出类似以下内容的内容:

+------+----------------+----------------+-----+
| item | SourceLocation | TargetLocation | Qty |
+------+----------------+----------------+-----+
|  100 | B              | A              |   3 |
|  100 | D              | A              |   1 |
|  100 | D              | C              |   2 |
|  200 | A              | B              |   2 |
|  200 | A              | C              |   3 |
|  200 | D              | C              |   1 |
|  300 | C              | A              |   3 |
|  300 | C              | B              |   3 |
+------+----------------+----------------+-----+

我当前连接两个表的查询如下:

select 
    n.*,
    (case when Ord <= Remainder and (RemaingNeed > 0 and RemaingNeed < RemainingInv) then Allocated + RemaingNeed else case when RemaingNeed < 0 then 0 else Allocated end end) as NeedToFill
from (
select
    n.*,
    row_number() over(partition by item order by RN, (case when need > Allocated then 0 else 1 end)) as Ord,
    n.TotalAvail - sum(n.Allocated) over (partition by item) as Remainder
from (
select 
    n.*,
    n.TotalAvail - sum(n.Allocated) over (partition by item order by RN) as RemainingInv,
    n.need - sum(n.Allocated) over (partition by item, location order by RN) as RemaingNeed
from (
  select
    n.*,
    case when Proportional > need then need else Proportional end as Allocated
  from (
    select
      row_number() over(order by need desc) as RN,
      n.*,
      h.location as Source,
      h.have,
      h.TotalAvail,
      convert(int, floor(h.have * n.need * 1.0 / n.TotalNeed), 0) as Proportional
    from (
      select n.*, sum(need) over (partition by item) as TotalNeed
      from NeedInv n) n
    join (select h.*, sum(have) over (partition by item) as TotalAvail from HaveInv h) h
      on n.item = h.item
      and h.have > 0
    ) n
  ) n
) n
) n
where n.need > 0

除了Allocated设置为零之外,它似乎在大多数情况下都有效,但是仍然有可以传输的项目。对于项目200 1可以看到这一点,其中位置B仅需要1但将要接收2个项目,而位置C还需要1的商品将收到0

任何帮助/指导将不胜感激!

2 个答案:

答案 0 :(得分:1)

IMO,您的查询看起来有点复杂。

据我所知,这只是使用运行中的库存总数将逻辑构建到查询中的简单问题。从本质上讲,这只是构建规则的问题,如果可以从源位置获取所需内容,则可以接受,否则,请尽可能多地使用。

例如,我相信以下查询包含所需的逻辑:

SELECT N.Item,
       SourceLocation = H.Location,
       TargetLocation = N.Location,
       Qty = 
        CASE 
            WHEN N.TotalRunningRequirement <= H.TotalRunningInventory -- If the current source location has enough stock to fill the request.
            THEN 
                CASE
                    WHEN N.TotalRunningRequirement - N.Need < H.TotalRunningInventory - H.Have -- If stock required has already been allocated from elsewhere.
                    THEN N.TotalRunningRequirement - (H.TotalRunningInventory - H.Have) -- Get the total running requirement minus stock allocated from elsewhere.
                    ELSE N.Need -- Otherwise just take how much is needed.
                END
            ELSE N.Need - (N.TotalRunningRequirement - H.TotalRunningInventory) -- Current source doesn't have enough stock to fulfil need, so take as much as possible.
        END
FROM
( 
    SELECT *, TotalRunningRequirement = SUM(need) OVER (PARTITION BY item ORDER BY location)
    FROM NeedInv
    WHERE need > 0
) AS N
JOIN 
(
    SELECT *, TotalRunningInventory = SUM(have) OVER (PARTITION BY item ORDER BY location)
    FROM HaveInv
    WHERE have > 0  
) AS H
    ON H.Item = N.Item
    AND H.TotalRunningInventory - (N.TotalRunningRequirement - N.need) > 0 -- Join if stock in source location can be taken
    AND H.TotalRunningInventory - H.Have - (N.TotalRunningRequirement - N.need) < N.TotalRunningRequirement
;

注意:据我所知,您想要的输出似乎与项目200的样本数据不匹配。

答案 1 :(得分:1)

我想知道是否可以将递归CTE用于分配。

但是事实却更加复杂。

结果与问题中的预期结果不完全一致。
但是由于其他答案返回的结果相同,所以我想很好。

因此,它只是一种额外的方法。

db <>小提琴here

上进行测试

基本上,它按照计算出的row_numbers的顺序遍历需求。
并根据需要分配可用的东西。

declare @HaveNeedInv table (
 item int,
 rn int,
 loc varchar(1),
 have int,
 need int,
 primary key (item, rn, loc, have, need)
);

insert into @HaveNeedInv (item, loc, have, need, rn)
select item, location, sum(have), 0 as need,
row_number() over (partition by item order by sum(have) desc)
from HaveInv
where have > 0
group by item, location;

insert into @HaveNeedInv (item, loc, have, need, rn)
select item, location, 0 as have, sum(need),
row_number() over (partition by item order by sum(need) desc)
from NeedInv
where need > 0
group by item, location;

;with ASSIGN as
(
    select h.item, 0 as lvl, 
     h.rn as hrn, n.rn as nrn,
     h.loc as hloc, n.loc as nloc, 
     h.have, n.need, 
     iif(h.have<=n.need,h.have,n.need) as assign 
    from @HaveNeedInv h
    join @HaveNeedInv n on (n.item = h.item and n.need > 0 and n.rn = 1)
    where h.have > 0 and h.rn = 1

    union all

    select t.item, a.lvl + 1,
     iif(t.have>0,t.rn,a.hrn),
     iif(t.need>0,t.rn,a.nrn),
     iif(t.have>0,t.loc,a.hloc),
     iif(t.need>0,t.loc,a.nloc),
     iif(a.have>a.assign,a.have-a.assign,t.have),
     iif(a.need>a.assign,a.need-a.assign,t.need),
     case 
     when t.have > 0
     then case 
          when t.have > (a.need - a.assign) then a.need - a.assign
          else t.have
          end
      else case 
           when t.need > (a.have - a.assign) then a.have - a.assign
           else t.need
           end
      end
    from ASSIGN a
    join @HaveNeedInv t 
      on t.item = a.item
    and iif(a.have>a.assign,t.need,t.have) > 0
    and t.rn = iif(a.have>a.assign,a.nrn,a.hrn) + 1
)
select 
item,
hloc as SourceLocation,
nloc as TargetLocation,
assign as Qty
from ASSIGN
where assign > 0
order by item, hloc, nloc
option (maxrecursion 1000);

结果:

100  B  A  3
100  D  A  1
100  D  C  2
200  A  B  1
200  D  C  1
300  C  A  3
300  C  B  5

在row_numbers中更改顺序(以填充@NeedHaveInv)将更改优先级,并可能返回不同的结果。