Справка по SQL-запросам для количеств очереди
-
06-07-2019 - |
Вопрос
У меня есть база данных, в которой есть таблица заказов и таблица запасов.
Таблица элементов заказа имеет 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, если у вас было местоположение. (иногда заказ может быть недоступен для выбора из-за проблем с количеством или статусом заказа, но вы все равно хотите, чтобы он был получен в результатах для целей отчетности или распределения) : Конец итерации
Я ценю помощь Стюарта Эйнсворта, я просто хотел избежать подзапросов и прочего. Мне удалось написать это, не делая никаких объединений к одним и тем же таблицам более одного раза и без подзапросов. Подбил твой, потому что это чертовски круто!