سؤال

لدي قاعدة بيانات تحتوي على جدول الطلبات وجدول المخزون.

يحتوي جدول عناصر الطلب على سجل واحد لكل تخطيط كمية واحدة، لذلك إذا قدم شخص طلبًا لـ 7 'ABC's و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 لست خبيرًا.
إم إس إس كيو إل 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.مذهل.

لسوء الحظ لا أستطيع نشر الكود، لكن الكود الزائف العام للحل هو:

قم بإنشاء متغير جدول ليحتوي على جميع مواقع الاختيار المتاحة والتي يمكن ربطها بأي شكل من الأشكال بأمر مفتوح.

قم بإنشاء متغير جدول ثانٍ ليحتوي على كافة الأوامر المفتوحة.قم بتضمين أي أعمدة تحتاجها لحالات العناصر الفردية في كل طلب.

قم بإنشاء جدول النتائج (أو قم بذلك أولاً، إذا كنت تستخدم دالة ذات قيمة جدولية) والذي يحتوي على المعلومات الضرورية لعملية الاختيار الخاصة بك.(لذا اطلب # و# العنصر و# والموقع الذي تريد سحبه منه.)

أعاد:

من عدد السجلات في الأوامر المفتوحة إلى 1 في جدول الأوامر المفتوحة، مع الانضمام إلى الموقع حيث الكمية > 0.قم بتخزين كل تمريرة في جدول النتائج.

قم بتقليل كمية الموقع الذي قمت بإدراجه للتو في جدول النتائج بمقدار 1، إذا كان لديك موقع.(في بعض الأحيان قد لا يكون الأمر قابلاً للانتقاء بسبب مشاكل الكمية أو العدالة ، لكنك ما زلت تريدها في نتائج أفعال التقارير أو التخصيص.): إنهاء التكرار

أقدر المساعدة التي قدمها ستيوارت أينسوورث، أردت فقط تجنب الاستعلامات الفرعية والأشياء.تمكنت من كتابة هذا دون القيام بأي صلة لنفس الجداول أكثر من مرة وبدون استعلامات فرعية.صدمتك لأنها رائعة جدًا!

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top