可能有很多方法可以解决这个问题,但我会尝试创建一个缩放函数,它将年份和 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