2011-10-20 11 views
9

Ho svolto ricerche sui CTE cercando di determinare se è possibile aggiornare in modo ricorsivo i record di quantità di magazzino con una quantità dell'ordine fino al consumo della quantità dell'ordine.CTE - aggiornamento ricorsivo della quantità fino al totale consumato

Qui ci sono le tabelle e record:

CREATE TABLE [dbo].[myOrder](
    [Account] [float] NOT NULL, 
    [Item] [float] NOT NULL, 
    [Quantity] [float] NOT NULL 
) ON [PRIMARY] 

insert into dbo.myOrder values (12345, 1, 50) 

CREATE TABLE [dbo].[myInventory](
    [ID] [int] IDENTITY(1,1) NOT NULL, 
    [Account] [float] NOT NULL, 
    [InvDate] [numeric](18, 0) NOT NULL, 
    [Item] [float] NOT NULL, 
    [Quantity] [float] NOT NULL, 
    [QuantitySold] [float] NOT NULL 
) ON [PRIMARY] 

insert into dbo.myInventory values (12345, 111287, 1, 45, 40) 
insert into dbo.myInventory values (12345, 111290, 1, 40, 0) 
insert into dbo.myInventory values (12345, 111290, 1, 12, 0) 
insert into dbo.myInventory values (12345, 111291, 1, 25, 0) 

Il record nella tabella Myorder indica che un ordine è necessario creare per conto 12345 per la voce # 1, la quantità 50:

Account Item Quantity 
------- ---- -------- 
12345 1 50 

la tabella di inventario dimostra che abbiamo un sacco di voce # 1 a portata di mano per conto 12345:

ID Account InvDate Item Quantity QuantitySold 
-- ------- ------- ---- -------- ------------ 
1 12345 111287 1 45  40 
2 12345 111290 1 40  0 
3 12345 111290 1 12  0 
4 12345 111291 1 25  0 

Goa Devo iniziare a collegare la quantità dell'ordine di 50 nei registri di inventario fino a quando tutti e 50 sono stati consumati. I record di inventario sono ordinati in base al valore nella colonna InvDate. Il record 1 ha 5 quantità rimanenti (45 - 40 = 5), il che ci lascerebbe con altri 45 da consumare per l'ordine. Record 2 può consumare 40. Record 3 può consumare l'ultimo 5. al termine della query i record di inventario sarebbe simile a questa:

ID Account InvDate Item Quantity QuantitySold 
-- ------- ------- ---- -------- ------------ 
1 12345 111287 1 45  45 
2 12345 111290 1 40  40 
3 12345 111290 1 12  5 
4 12345 111291 1 25  0 

Nota: La tabella contiene l'inventario QuantitySold, non QuantityRemaining, in modo da avere per fare il matematica (Quantità - Quantità venduta) per determinare la quantità rimanente per record di inventario.

Ho ottenuto quasi nulla con il CTE. Ho trovato un sacco di esempi per fare selezioni in cui si hanno 2 parti per il CTE - una parte di inizializzazione e la parte ricorsiva UNIONed insieme. Potrei scrivere questo con un cursore, ma penso che sia possibile fare con un CTE e mi piacerebbe sapere come.

Se qualcuno può confermare ciò è possibile con un CTE o spiegare come impostare il CTE, lo apprezzerei. Grazie!

+3

+1 Per lo script DDL –

+3

Off topic, ma perché stai usando * float * per modellare la quantità? Vuoi davvero * essere in grado di avere in magazzino il 1.47E-19 dell'articolo 2? –

+0

Damien: giusta domanda. La mia azienda utilizza JDEdwards come suo ERP, che utilizza float per i suoi campi di quantità. Ho costruito questi tavoli con questo in mente. Ah, database legacy! – Brian

risposta

10
[email protected] table mimics inserted virtual table from AFTER INSERT triggers on [dbo].[myOrder] table 
DECLARE @inserted TABLE 
(
    [Account] [float] NOT NULL, 
    [Item] [float] NOT NULL, 
    [Quantity] [float] NOT NULL 
); 

INSERT @inserted 
VALUES (12345, 1, 50); 

WITH CteRowNumber 
AS 
(
    SELECT inv.ID 
      ,inv.Account 
      ,inv.Item 
      ,inv.Quantity 
      ,inv.QuantitySold 
      ,i.Quantity QuantityOrdered 
      ,ROW_NUMBER() OVER(PARTITION BY inv.Account,inv.Item ORDER BY inv.ID ASC) RowNumber 
    FROM myInventory inv 
    INNER JOIN @inserted i ON inv.Account = i.Account 
    AND  inv.Item = i.Item 
    WHERE inv.Quantity > inv.QuantitySold 
), CteRecursive 
AS 
(
    SELECT a.ID 
      ,a.Account 
      ,a.Item 
      ,a.RowNumber 
      ,CASE 
       WHEN a.Quantity - a.QuantitySold < a.QuantityOrdered THEN a.Quantity - a.QuantitySold 
       ELSE a.QuantityOrdered 
      END QuantitySoldNew 
      ,CASE 
       WHEN a.Quantity - a.QuantitySold < a.QuantityOrdered THEN a.Quantity - a.QuantitySold 
       ELSE a.QuantityOrdered 
      END RunningTotal 
    FROM CteRowNumber a 
    WHERE a.RowNumber = 1 
    UNION ALL 
    SELECT crt.ID 
      ,crt.Account 
      ,crt.Item 
      ,crt.RowNumber 
      ,CASE 
       WHEN prev.RunningTotal + (crt.Quantity - crt.QuantitySold) < crt.QuantityOrdered THEN crt.Quantity - crt.QuantitySold 
       ELSE crt.QuantityOrdered - prev.RunningTotal 
      END QuantitySoldNew 
      ,CASE 
       WHEN prev.RunningTotal + (crt.Quantity - crt.QuantitySold) < crt.QuantityOrdered THEN prev.RunningTotal + (crt.Quantity - crt.QuantitySold) 
       ELSE crt.QuantityOrdered 
      END RunningTotal 
    FROM CteRecursive prev 
    INNER JOIN CteRowNumber crt ON prev.Account = crt.Account 
    AND  prev.Item = crt.Item 
    AND  prev.RowNumber + 1 = crt.RowNumber 
    WHERE prev.RunningTotal < crt.QuantityOrdered 
) 
SELECT cte.ID 
     ,cte.Account 
     ,cte.Item 
     ,cte.QuantitySoldNew 
FROM CteRecursive cte; 
--or CteRecursive can be used to update QuantitySold column from [dbo].[myInventory] table 
--UPDATE myInventory 
--SET  QuantitySold = inv.QuantitySold + cte.QuantitySoldNew 
--FROM myInventory inv 
--INNER JOIN CteRecursive cte ON inv.ID = cte.ID; 
+0

Bogdan, è fantastico! Grazie mille. Ho bisogno di passare un po 'di tempo a studiare la tua query per capire tutto ciò che sta facendo, ma funziona benissimo. È davvero bello vederlo funzionare e consumare correttamente la quantità dell'ordine. Grazie ancora! – Brian

Problemi correlati