Вопрос

У меня есть база данных, в которой есть таблица заказов и таблица запасов.

Таблица элементов заказа имеет 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

Это было полезно?

Решение

Уфф, это было тяжело для меня; Я уверен, что есть более элегантные решения, чем это, но вот что я придумал:

--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. Высокий.

К сожалению, я не могу опубликовать код, но общий псевдокод для решения:

Создайте переменную таблицы, которая будет содержать все доступные местоположения выбора, которые можно каким-либо образом привязать к открытому заказу.

Создайте вторую переменную таблицы, которая будет содержать все открытые ордера. Включите все необходимые столбцы для статусов отдельных элементов в каждом заказе.

Создайте таблицу результатов (или сделайте это сначала, если вы используете табличную функцию), которая содержит необходимую информацию для процесса выбора. (Так что заказывайте #, item # и из какого местоположения # вы хотите, чтобы он вытащил.)

Итерация:

от количества записей в открытых ордерах до 1 таблицы открытых ордеров, соединяясь там, где у местоположения есть qty > 0. Сохраните каждый проход в таблицу результатов.

Уменьшите количество местоположений, которые вы только что вставили в таблицу результатов, на 1, если у вас было местоположение. (иногда заказ может быть недоступен для выбора из-за проблем с количеством или статусом заказа, но вы все равно хотите, чтобы он был получен в результатах для целей отчетности или распределения) : Конец итерации

Я ценю помощь Стюарта Эйнсворта, я просто хотел избежать подзапросов и прочего. Мне удалось написать это, не делая никаких объединений к одним и тем же таблицам более одного раза и без подзапросов. Подбил твой, потому что это чертовски круто!

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top