如何使用公式创建表来计算第二个表中的数据?

时间:2013-05-12 16:13:20

标签: sql sql-server tsql

我有一个包含1000个产品的ProductTable。例如,下面只有7个产品。产品1-5的数据通过脚本从源导入到表中。我将使用此数据计算其他产品的数据。

然后我有第二个表,其中包含公式,用于计算第一个表中产品的数据。会有很多产品,每个产品都会有一定的配方。例如,下面是2个产品。 此表不使用数字,而是使用指向第一个表的链接。数字是第一张表中的productsID。

第三个表格是为了清楚地理解并显示应该如何进行计算。这不是理想的结果。结果将是这些计算的结果。 (对于ProductID6和year1999,结果应为3;对于productID6和year2001,结果应为1.75,等等。)

1

如何编写存储过程来捕获这些公式fork。所以基本上我想要存储计算产品的公式。并且,如果需要,可以更改公式并重新计算结果。

很高兴听到您的想法。

1 个答案:

答案 0 :(得分:2)

可能有很多方法可以解决这个问题,但我会尝试创建一个将年份和ProductID作为参数并返回单个映射公式的缩放器函数。

-- Assumption is a Product table structured like this: Product(ProductID INT, [1999] INT, [2000] INT, [2001] INT);
CREATE FUNCTION dbo.MapFn(@Y INT, @ProductID INT)
RETURNS VARCHAR(200)
AS
BEGIN
    DECLARE @A VARCHAR(10), @B VARCHAR(10), @C VARCHAR(10);
    DECLARE @pvt TABLE(ProductID INT, Yr INT, Qty VARCHAR(10));
    DECLARE @f VARCHAR(80);

    INSERT @pvt
    SELECT ProductID, Yr, Qty
    FROM (
        SELECT ProductID, [1999], [2000], [2001]
        FROM dbo.Product) p
    UNPIVOT (
        Qty FOR Yr IN ([1999], [2000], [2001])
    ) AS u
    WHERE Yr = @Y;

    SELECT @f = formula FROM dbo.Formula WHERE ProductID = @ProductID
    SELECT @A = p.Qty + '.0' FROM @pvt p JOIN dbo.Formula f ON f.ProductID = @ProductID AND f.A = p.ProductID;
    SELECT @B = p.Qty + '.0' FROM @pvt p JOIN dbo.Formula f ON f.ProductID = @ProductID AND f.B = p.ProductID;
    SELECT @C = p.Qty + '.0' FROM @pvt p JOIN dbo.Formula f ON f.ProductID = @ProductID AND f.C = p.ProductID;

    RETURN REPLACE(REPLACE(REPLACE(@f,'A', ISNULL(CAST(@A AS VARCHAR(50)), '')), 'B', ISNULL(CAST(@B AS VARCHAR(50)), '')), 'C', ISNULL(CAST(@C AS VARCHAR(50)), ''));
END
GO

该功能对于UNPIVOT中的固定年份列表很脆弱,但您可以通过在列表中添加更多年来解决这个问题。

使用示例

CREATE TABLE dbo.Product(ProductID INT, [1999] INT, [2000] INT, [2001] INT);
GO
CREATE TABLE dbo.Formula(ProductID INT, formula VARCHAR(80), [A] INT, [B] INT, [C] INT);
GO

INSERT dbo.Product values(1,10,20,30)
, (2,15,25,35)
, (3,5,15,20)
, (4,4,8,12)
, (5,6,12,18)
, (6,NULL,NULL,NULL)
, (7,NULL,NULL,NULL);

INSERT dbo.Formula VALUES(6,'A/B',2,3,NULL)
, (7,'A/(B+C)',5,3,1);
GO

CREATE FUNCTION dbo.MapFn(@Y INT, @ProductID INT)
RETURNS VARCHAR(200)
AS
BEGIN
    DECLARE @A VARCHAR(10), @B VARCHAR(10), @C VARCHAR(10);
    DECLARE @pvt TABLE(ProductID INT, Yr INT, Qty VARCHAR(10));
    DECLARE @f VARCHAR(80);

    INSERT @pvt
    SELECT ProductID, Yr, Qty
    FROM (
        SELECT ProductID, [1999], [2000], [2001]
        FROM dbo.Product) p
    UNPIVOT (
        Qty FOR Yr IN ([1999], [2000], [2001])
    ) AS u
    WHERE Yr = @Y;

    SELECT @f = formula FROM dbo.Formula WHERE ProductID = @ProductID
    SELECT @A = p.Qty + '.0' FROM @pvt p JOIN dbo.Formula f ON f.ProductID = @ProductID AND f.A = p.ProductID;
    SELECT @B = p.Qty + '.0' FROM @pvt p JOIN dbo.Formula f ON f.ProductID = @ProductID AND f.B = p.ProductID;
    SELECT @C = p.Qty + '.0' FROM @pvt p JOIN dbo.Formula f ON f.ProductID = @ProductID AND f.C = p.ProductID;

    RETURN REPLACE(REPLACE(REPLACE(@f,'A', ISNULL(CAST(@A AS VARCHAR(50)), '')), 'B', ISNULL(CAST(@B AS VARCHAR(50)), '')), 'C', ISNULL(CAST(@C AS VARCHAR(50)), ''));
END
GO

-- Cursor loop to evaluate formulas
DECLARE @ProductID INT;
DECLARE @fa NVARCHAR(80);
DECLARE @fb NVARCHAR(80);
DECLARE @fc NVARCHAR(80);
DECLARE @sql NVARCHAR(160);
DECLARE @A DECIMAL(10,2), @B DECIMAL(10,2), @C DECIMAL(10,2)
DECLARE @resultSet TABLE(ProductID INT, [1999] DECIMAL(10,2), [2000] DECIMAL(10,2), [2001] DECIMAL(10,2));
DECLARE c CURSOR FOR 
    SELECT f.ProductID
    , [1999] = dbo.MapFn(1999,f.ProductID)
    , [2000] = dbo.MapFn(2000,f.ProductID)
    , [2001] = dbo.MapFn(2001,f.ProductID)
    FROM dbo.Formula f;
OPEN c
FETCH NEXT FROM c INTO @ProductID, @fa, @fb, @fc;

WHILE @@FETCH_STATUS = 0 BEGIN
    -- 1999
    SET @sql = N'SELECT @out = '+@fa;
    EXECUTE sp_executesql @sql, @params = N'@out DECIMAL(10,2) OUTPUT', @out = @A OUTPUT;

    -- 2000
    SET @sql = N'SELECT @out = '+@fb;
    EXECUTE sp_executesql @sql, @params = N'@out DECIMAL(10,2) OUTPUT', @out = @B OUTPUT;

    -- 2001
    SET @sql = N'SELECT @out = '+@fc;
    EXECUTE sp_executesql @sql, @params = N'@out DECIMAL(10,2) OUTPUT', @out = @C OUTPUT;

    INSERT @resultSet VALUES(@ProductID, @A, @B, @C);
    FETCH NEXT FROM c INTO @ProductID, @fa, @fb, @fc;
END
SELECT * FROM @resultSet;
CLOSE c;
DEALLOCATE c;

<强>结果

ProductID   1999    2000    2001
6           3.00    1.67    1.75
7           0.40    0.34    0.36