1

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

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

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

1

如何编写存储过程来捕获这些公式分叉。所以基本上我想找到一种方法来存储计算产品的公式。并且,如果需要,能够更改公式并重新计算结果。

很高兴听到你的想法。

4

1 回答 1

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
于 2013-05-12T22:17:02.370 回答