我有一个包含订单表和库存表的数据库。
order-items表每1 qty布局有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
答案 0 :(得分:0)
哇,这对我来说很艰难;我确信有比这更优雅的解决方案,但这就是我提出的:
--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 :(得分:0)
在重新审视这个问题之后,我认为创建一个表值函数会更有效率
现在的查询从1:45到0:03。真棒。
不幸的是我无法发布代码,但解决方案的一般伪代码是:
创建表变量以包含所有可用的选择位置,这些位置可以任何方式与未结订单相关联。
创建第二个表变量以包含所有未结订单。包括每个订单上各个项目状态所需的任何列。
创建结果表(如果您使用的是表值函数,则首先执行此操作),其中包含选择过程的必要信息。 (所以命令#,item#,以及你希望它从哪个位置拉出来。)
迭代:
从未结订单中的记录数到1个未结订单表,加入位置有数量&gt;的位置0.将每个传递存储到结果表中。
如果您有位置,请将刚刚插入结果表中的位置数量减少1。 (有时订单可能因数量或订单状态问题而无法选择,但您仍然希望将它们用于报告或分配目的。) :结束迭代
我很感谢Stuart Ainsworth的帮助,我只是想避免子查询和东西。我设法写了这个,没有多次连接到同一个表,没有子查询。把它吓到了,因为它太棒了!