문제

주문 테이블 및 인벤토리 테이블이있는 데이터베이스가 있습니다.

Order-Items 테이블에는 1 수량 레이아웃 당 1 개의 레코드가 있으므로 사람이 7 'ABC와 4'XYZ를 주문하면 테이블에 11 개의 레코드가 나타납니다.

id, item, qtyNum
01  ABC   1 
02  ABC   2 
03  ABC   3 
04  ABC   4 
05  ABC   5
06  ABC   6
07  ABC   7
08  XYZ   1
09  XYZ   2
10  XYZ   3
11  XYZ   4

인벤토리 테이블에는 수량/항목 별 위치 레이아웃이 있으므로 20 개의 재고가있을 수 있지만 20 개의 개별 위치에서 (최악의 경우) 일 수 있습니다. 따라서이 예에서는 다음과 같은 인벤토리가있을 수 있습니다.

Qty, Item,             Loc,          Date
3    'ABC' in Location L1  with date 1990
2    'ABC' in Location L2  with date 1992
5    'ABC' in Location L3  with date 2003
4    'ABC' in Location LH  with date 2004
1    'XYZ' in Location L4  with date 1990
2    'XYZ' in Location L5  with date 1993
9    'XYZ' in Location L6  with date 2001
2    'XYZ' in Location LJ  with date 2004

*H와 J는 특별한 의미가 없습니다! 그들이 가장 새롭다는 점을 집으로 몰아 넣습니다.

결과 세트는 먼저 가장 오래된 위치에서 가능한 한 많은 것을 가져와야 하므로이 예에서는 다음과 같은 '픽 큐'가 끝납니다.

Pick 3 'ABC' from L1
Pick 2 'ABC' from L2
Pick 2 'ABC' from L3
Pick 1 'XYZ' from L4
Pick 2 'XYZ' from L5
Pick 1 'XYZ' from L6

나는 외부 결합과 그와 같은 미친 것들로 여러 번 결합 된 많은 견해를 포함하는 솔루션이 있으며이 문제에 대한 간단한/우아한 솔루션이 있는지 궁금합니다. 코드로 문제가 없지만 SQL에서는 전문가가 아닙니다.
MSSQL 2008

도움이 되었습니까?

해결책

Whew, 이것은 나에게 힘든 일이었다. 나는 이것보다 더 우아한 솔루션이 있다고 확신하지만, 이것이 내가 생각한 것입니다.

--test data
DECLARE @orders TABLE
    (
      ID INT IDENTITY(1, 1) ,
      item CHAR(3) ,
      Qty INT
    )
INSERT  INTO @orders
        ( item, Qty )
VALUES  ( 'abc', 1 ),
        ( 'abc', 2 ),
        ( 'abc', 3 ),
        ( 'abc', 4 ),
        ( 'abc', 5 ),
        ( 'abc', 6 ),
        ( 'abc', 7 ),
        ( 'xyz', 1 ),
        ( 'xyz', 2 ),
        ( 'xyz', 3 ),
        ( 'xyz', 4 )

DECLARE @ItemLoc TABLE
    (
      Qty INT ,
      ITEM CHAR(3) ,
      Loc CHAR(2) ,
      Dt INT
    )
INSERT  INTO @ItemLoc
        ( Qty, ITEM, Loc, Dt )
VALUES  ( 3, 'abc', 'L1', 1990 ),
        ( 2, 'abc', 'L2', 1992 ),
        ( 5, 'abc', 'L3', 2003 ),
        ( 4, 'abc', 'LH', 2004 ),
        ( 1, 'xyz', 'L4', 1990 ),
        ( 2, 'xyz', 'L5', 1993 ),
        ( 9, 'xyz', 'L6', 2001 ),
        ( 2, 'xyz', 'LJ', 2004 ) ;


/*looks complicated, and it is
    I use a cte to try to ease it up a bit,
    but I first identify a running sum of items 
    in the bins, and a pull order based on item 
    and year.
*/  


WITH    cte
          AS ( SELECT   a.Qty ,
                        a.Item ,
                        a.Loc ,
                        a.Dt ,
                        a.RunningSum ,
                        a.PullOrder ,
                        b.Qty AS OrderQty
               FROM     ( SELECT    Qty ,
                                    Item ,
                                    Loc ,
                                    Dt ,
                                    RunningSum = ( SELECT   SUM(Qty)
                                                   FROM     @ItemLoc il1
                                                   WHERE    il1.Item = il.Item
                                                            AND il1.Dt <= il.Dt
                                                 ) ,
                                    PullOrder = ROW_NUMBER() OVER ( PARTITION BY Item ORDER BY Dt )
                          FROM      @ItemLoc il
                        ) a
                        JOIN ( SELECT   item ,
                                        MAX(qty) AS qty
                               FROM     @orders o
                               GROUP BY item
                             ) b ON a.Item = b.item
             )
    /* I then use the cte to a) identify the minimum bin
       which has a RunningSum of items greater than the OrderQty,
       and b) pick all of the items in the bins below that, and 
       c) pick the remaining items from the last bin
   */         


    SELECT  Pick = CASE WHEN RunningSum <= OrderQty THEN Qty
                        ELSE OrderQty - ( SELECT    SUM(Qty)
                                          FROM      cte c3
                                          WHERE     c3.item = c1.ITem
                                                    AND c3.RunningSum < c1.RunningSum
                                        )
                   END ,
            c1.Item ,
            Loc
    FROM    cte c1
            JOIN ( SELECT   Item ,
                            MIN(PullOrder) AS po
                   FROM     cte c2
                   WHERE    RunningSum >= OrderQty
                   GROUP BY Item
                 ) x ON c1.Item = x.Item
                        AND c1.PullOrder <= x.po

다른 팁

이 문제를 다시 방문한 후, 나는 테이블 값 기능을 만드는 것이 훨씬 더 효율적이라고 결정하고

쿼리는 지금 당장 1:45에서 0:03으로갔습니다. 대박.

불행히도 코드를 게시 할 수는 없지만 솔루션의 일반적인 의사 코드는 다음과 같습니다.

테이블 변수를 생성하여 공개 순서와 관련이있는 모든 사용 가능한 픽 위치를 포함합니다.

모든 공개 주문을 포함하는 두 번째 테이블 변수를 만듭니다. 각 순서에서 개별 항목의 상태에 필요한 열을 포함하십시오.

픽 프로세스에 필요한 정보가 포함 된 결과 테이블을 만들거나 테이블 값 기능을 사용하는 경우 먼저 수행하십시오. (따라서 주문#, 항목#, 그리고 어떤 위치#를 가져 가고 싶은지.)

반복 :

공개 주문의 레코드 수에서 Open Orders 테이블의 1 개에 이르기까지 위치에 Qty> 0이있는 위치에 가입하십시오. 각 패스를 결과 테이블에 저장하십시오.

위치가있는 경우 방금 결과 테이블에 방금 삽입 한 위치의 수량을 1 씩 줄입니다. (때로는 수량 또는 Orderstatus 문제로 인해 주문이 양해질 수 없지만보고 또는 할당 목적으로 결과를 원합니다.) : 반복 종료

Stuart Ainsworth의 도움에 감사드립니다. 단지 서브 쿼리와 물건을 피하고 싶었습니다. 나는 같은 테이블에 두 번 이상 조인하지 않고 서브 쿼리없이 이것을 쓸 수있었습니다. 끔찍하기 때문에 당신의 부딪 쳤습니다!

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top