Domanda

Ho un database che ha una tabella degli ordini e una tabella di inventario.

La tabella degli articoli dell'ordine ha un record 1 per 1 qtà layout, quindi se una persona effettua un ordine per 7 'ABC e 4' XYZ, ottengo 11 record nella tabella.

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

La tabella di inventario ha un layout quantità / articolo per posizione, quindi posso avere 20 di qualcosa in magazzino, ma può essere (nel peggiore dei casi) in 20 posizioni separate. Quindi, per il nostro esempio, potrei avere il seguente inventario:

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

* La H e la J non hanno alcun significato speciale! Sto solo portando il punto a casa che sono i più recenti

Il set di risultati dovrebbe prima prelevare il maggior numero possibile dalle posizioni più vecchie, quindi per questo esempio finisco con la seguente 'pick Queue':

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

Ho una soluzione che coinvolge molte visualizzazioni che vengono unite più volte con join esterni e cose folli come quella e sono solo curioso di sapere se esiste una soluzione semplice / elegante per questo problema? Potrei farlo nel codice senza problemi, ma in SQL non sono un guru.
MSSQL 2008

È stato utile?

Soluzione

Accidenti, questo è stato difficile per me; Sono sicuro che ci sono soluzioni più eleganti di questa, ma questo è quello che mi è venuto in mente:

--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

Altri suggerimenti

Dopo aver rivisitato questo problema, ho deciso che sarebbe stato molto più efficiente creare una funzione con valori di tabella e

La query così com'è ora è passata dalle 1:45 alle 0:03. Impressionante.

Sfortunatamente non posso pubblicare il codice, ma lo pseudo codice generale per la soluzione è:

Crea una variabile di tabella per contenere tutte le posizioni di prelievo disponibili che possono essere in qualche modo legate a un ordine aperto.

Crea una seconda variabile di tabella per contenere tutti gli ordini aperti. Includi tutte le colonne necessarie per gli stati dei singoli articoli in ciascun ordine.

Crea la tabella dei risultati (o esegui prima questa operazione, se stai utilizzando una funzione con valori di tabella) che contiene le informazioni necessarie per il processo di selezione. (Quindi ordina #, oggetto # e da quale posizione # desideri estrarre.)

Iterate:

dal numero di record negli ordini aperti a 1 della tabella degli ordini aperti, unendo dove la posizione ha qty > 0. Memorizzare ciascun passaggio nella tabella dei risultati.

Riduci di 1 la quantità della posizione che hai appena inserito nella tabella dei risultati, se avevi una posizione. (a volte un ordine potrebbe non essere selezionabile a causa di problemi relativi alla quantità o allo stato degli ordini, ma li desideri comunque nei risultati a scopo di reportistica o allocazione.) : Termina Iterate

Apprezzo l'aiuto Stuart Ainsworth, volevo solo evitare domande secondarie e cose del genere. Sono riuscito a scrivere questo senza fare alcun join alle stesse tabelle più di una volta e senza sub query. Incontrato il tuo perché è fantastico!

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top