也许有很多方法可以解决这个问题,但我会尝试创建一个将年份和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 = '[email protected];
EXECUTE sp_executesql @sql, @params = N'@out DECIMAL(10,2) OUTPUT', @out = @A OUTPUT;
-- 2000
SET @sql = N'SELECT @out = '[email protected];
EXECUTE sp_executesql @sql, @params = N'@out DECIMAL(10,2) OUTPUT', @out = @B OUTPUT;
-- 2001
SET @sql = N'SELECT @out = '[email protected];
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
因此,每个产品有5个输入行和两个输出行? – Paparazzi 2013-05-12 19:59:16
没有。有超过1000种产品。但其中一些是通过脚本插入的,另一些是从第一个开始计算的。所以我想存储公式来计算这些产品。 – Almazini 2013-05-13 05:56:28
每个产品都是独一无二的,但每年都在变化。想象一下食品价格:牛奶,面包,啤酒,奶酪和咖啡的价格。 – Almazini 2013-05-13 06:16:01