2009-10-23 5 views
0

J'ai une base de données qui a une table de commandes et une table d'inventaire.

La table des éléments de commande a une mise en page de 1 enregistrement par 1, donc si une personne passe une commande pour 7 'ABC et 4' XYZ, j'obtiens 11 enregistrements dans la table.Aide de requête SQL pour les quantités de file d'attente

 
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 

Le tableau d'inventaire a une quantité/mise en page par élément-lieu, donc je peux avoir 20 quelque chose en stock, mais il peut être (dans le pire des cas) dans 20 endroits différents. Ainsi pour notre exemple, je pourrais avoir l'inventaire suivant:

 
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 

* Les H et J n'ont aucune signification spéciale! Juste conduire la maison point qu'ils sont les plus récents

Le jeu de résultats doit tirer le plus grand nombre possible des lieux les plus anciens d'abord, donc pour cet exemple, je finis avec le texte suivant: « file d'attente choisissez »:

 
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 

J'ai une solution qui implique beaucoup de vues qui sont jointes à plusieurs reprises avec des jointures externes et des trucs fous comme ça et je suis juste curieux de savoir s'il existe une solution simple/élégante pour ce problème? Je pourrais le faire dans le code sans problème, mais en SQL, je ne suis pas un gourou.
MSSQL 2008

+0

Dans votre exemple de la table order-items, le qtyNum devrait-il augmenter chaque rangée, ou devrait-il être 1 dans chaque rangée. S'ils placent une commande pour 7 articles d'ABC, il semblerait que si vous avez pris le tableau, 28 serait la quantité. Ou est-ce que je ne comprends pas le point de la colonne qtyNum? –

+0

Il suffit de données supplémentaires pour mentionner la commande des articles. Son id-article 10 est la troisième quantité pour l'article 'XYZ' sur cet ordre. Pour toutes fins utiles, peut probablement simplement ignorer qtyNum – Hatch

Répondre

0

Ouf, c'était un dur pour moi; Je suis sûr qu'il ya des solutions plus élégantes que cela, mais c'est ce que je suis venu avec:

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

Cela semble génial! Je vais revoir cette solution dès que j'aurai quelques minutes. Merci! – Hatch

0

Après avoir revisité ce problème, j'ai décidé que ce serait beaucoup plus efficace de créer une fonction à valeur de table et

La requête actuelle est passée de 1:45 à 0:03. Impressionnant.

Malheureusement, je ne peux pas afficher le code, mais le code pseudo général pour la solution est:

Créer une variable de table pour contenir tous les lieux de prélèvement disponibles qui peuvent être en aucune façon liée à une commande ouverte.

Créez une deuxième variable de table pour contenir tous les ordres ouverts. Inclure toutes les colonnes dont vous avez besoin pour les statuts des articles individuels sur chaque commande.

Créez le tableau de résultats (ou faites-le en premier, si vous utilisez une fonction table) qui contient les informations nécessaires pour votre processus de sélection. (Commandez #, article #, et quel endroit # vous voulez qu'il tirer de.)

Itérer:

du nombre d'enregistrements dans les commandes ouvertes à 1 sur la table des commandes ouvertes, rejoignant où l'emplacement a qté> 0. Stocke chaque passe dans la table des résultats.

Réduisez la quantité de l'emplacement que vous venez d'insérer dans la table de résultats par 1, si vous avez un emplacement. (Parfois, une commande peut ne pas être sélectionnable en raison de problèmes de quantité ou d'ordre, mais vous voulez quand même les voir dans les résultats pour fins de reporting ou d'allocation.) : Fin Iterate

J'apprécie l'aide de Stuart Ainsworth, je voulais juste éviter sous requêtes et trucs.J'ai réussi à écrire ceci sans faire de jointures aux mêmes tables plus d'une fois et sans sous-requêtes. Bumped vôtre parce que son monstre génial!