2011-10-20 5 views
9

He estado investigando CTE tratando de determinar si es posible actualizar recursivamente los registros de cantidad de inventario con una cantidad de orden hasta que se consuma la cantidad de la orden.CTE - cantidad de actualización recursiva hasta el total consumido

Estas son las tablas y registros:

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) 

El registro de la tabla Myorder indica que una orden se va a crear para la cuenta 12345 para el punto # 1, cantidad 50:

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

La tabla de inventario muestra que tenemos a mano un gran número de productos para la cuenta 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 

The goa l debe comenzar a introducir la cantidad de pedido de 50 en los registros de inventario hasta que se consuman los 50. Los registros de inventario están ordenados por el valor en la columna InvDate. El registro 1 tiene 5 cantidades restantes (45 - 40 = 5), lo que nos dejaría con 45 más para consumir para el pedido. Ficha 2 puede consumir 40. Registro 3 puede consumir el último 5. Cuando se complete la consulta los registros de inventario se vería así:

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 tabla almacena el inventario QuantitySold, no QuantityRemaining, por lo que tiene que hacer el math (Quantity - QuantitySold) para determinar la cantidad que queda por registro de inventario.

No he llegado casi a ninguna parte con el CTE. He encontrado muchos ejemplos para hacer selecciones en las que tienes 2 partes para tu CTE: una parte de inicialización y la parte recursiva UNIONed. Podría escribir esto con un cursor, pero creo que es posible hacerlo con un CTE y me gustaría aprender cómo hacerlo.

Si alguien puede confirmar que esto es posible con un CTE o explicar cómo configurar el CTE, se lo agradecería. ¡Gracias!

+3

+1 Para secuencia de comandos DDL –

+3

Fuera de tema, pero ¿por qué está usando * * flotador para modelar la cantidad? ¿Realmente * desea * poder tener 1.47E-19 del artículo 2 en stock? –

+0

Damien - buena pregunta. Mi empresa utiliza JDEdwards como su ERP, que usa flotantes para sus campos de cantidad. Construí estas tablas con eso en mente. Ah, bases de datos heredadas! – Brian

Respuesta

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, ¡esto es fantástico! Muchas gracias. Necesito pasar algún tiempo estudiando su consulta para entender todo lo que está haciendo, pero funciona muy bien. Es realmente genial verlo funcionar y consumir la cantidad de la orden correctamente. ¡Gracias de nuevo! – Brian

Cuestiones relacionadas