我有两张桌子
WAC表
ID wac_inc item
-- ----------------- ----
1 2.310000000000000 A
2 1.100000000000000 A
3 2.130000000000000 A
4 1.340000000000000 A
基线表
item baseline
---- ------------------
A 10.000000000000000
预期结果
ID wac_inc item Running_Mul
-- ----------------- ---- -----------
1 2.310000000000000 A 10.231 -- 10 * (1+(2.310000000000000/100))
2 1.100000000000000 A 10.343541 -- 10.231 * (1+(1.100000000000000/100))
3 2.130000000000000 A 10.563858 -- 10.343541 * (1+(2.130000000000000/100))
4 1.340000000000000 A 10.705413 -- 10.563858 * (1+(1.340000000000000/100))
查找running_mul
的公式是
基线*(1 +(wac_inc / 100))
此处每行上一行Running_Mul
值为baseline
,第一行baseline
将来自baseline table
。
希望我说清楚。 AFAIK我们可以使用CURSOR
执行此操作,但我希望尽可能避免 RBAR
。
任何人都可以建议我采用更好的方法。
答案 0 :(得分:8)
尝试:
DECLARE @t TABLE
(
ID INT ,
wac DECIMAL(30, 10) ,
item CHAR(1)
)
DECLARE @b TABLE
(
item CHAR(1) ,
baseline DECIMAL(30, 10)
)
INSERT INTO @t
VALUES ( 1, 2.31, 'A' ),
( 2, 1.10, 'A' ),
( 3, 2.13, 'A' ),
( 4, 1.34, 'A' )
INSERT INTO @b
VALUES ( 'A', 10 );
WITH ordercte
AS ( SELECT * ,
ROW_NUMBER() OVER ( PARTITION BY item ORDER BY ID ) AS rn
FROM @t
),
rec
AS ( SELECT t.item ,
t.ID ,
t.wac ,
t.rn ,
b.baseline * ( 1 + ( t.wac / 100 ) ) AS m
FROM ordercte t
JOIN @b b ON b.item = t.item
WHERE t.rn = 1
UNION ALL
SELECT t.item ,
t.ID ,
t.wac ,
t.rn ,
c.m * ( 1 + ( t.wac / 100 ) )
FROM ordercte t
JOIN rec c ON t.item = c.item
AND t.rn = c.rn + 1
)
SELECT id ,
wac ,
item ,
m
FROM rec
输出:
id wac item m
1 2.3100000000 A 10.231000
2 1.1000000000 A 10.343541
3 2.1300000000 A 10.563858
4 1.3400000000 A 10.705414
<强> EDIT1 强>
我试图实施LOG EXP技巧,但除非@usr引导我解决问题,否则无法管理。所以用户@usr的所有积分:
WITH ordercte
AS ( SELECT t.ID ,
t.wac ,
t.item ,
b.baseline ,
ROW_NUMBER() OVER ( PARTITION BY t.item ORDER BY ID ) AS rn
FROM @t t
JOIN @b b ON b.item = t.item
)
SELECT baseline
* EXP(SUM(LOG(( 1 + ( wac / 100 ) ))) OVER ( PARTITION BY item ORDER BY rn )) AS m
FROM ordercte
或者只是:
SELECT t.ID, t.wac, t.item, baseline
* EXP(SUM(LOG(( 1 + ( wac / 100 ) ))) OVER ( PARTITION BY t.item ORDER BY t.ID )) AS m
FROM @t t
JOIN @b b ON b.item = t.item
如果ID是您订购的字段。
输出:
ID wac item m
1 2.3100000000 A 10.231
2 1.1000000000 A 10.343541
3 2.1300000000 A 10.5638584233
4 1.3400000000 A 10.7054141261722
<强> EDIT2 强>
对于SQL 2008,请使用:
WITH cte
AS ( SELECT t.ID ,
t.wac ,
t.item ,
baseline ,
( SELECT SUM(LOG(( 1 + ( wac / 100 ) )))
FROM @t it
WHERE it.item = t.item AND it.ID <= t.ID
) AS e
FROM @t t
JOIN @b b ON b.item = t.item
)
SELECT ID, wac, item, baseline * EXP(e) AS m
FROM cte
<强> EDIT3 强>
这是SQL Server 2008的完整解决方案,使用NULL和负值拨号:
WITH cte
AS ( SELECT t.ID ,
t.wac ,
t.item ,
b.baseline ,
ca.e,
ca.n,
ca.m
FROM @t t
JOIN @b b ON b.item = t.item
CROSS APPLY(SELECT SUM(LOG(ABS(NULLIF( 1 + wac / 100 , 0)))) as e,
SUM(SIGN(CASE WHEN 1 + wac / 100 < 0 THEN 1 ELSE 0 END)) AS n,
MIN(ABS(1 + wac / 100)) AS m
FROM @t it
WHERE it.item = t.item AND it.ID <= t.ID
) ca
)
SELECT ID, wac, item, baseline *
CASE
WHEN m = 0 THEN 0
WHEN n % 2 = 1 THEN -1 * EXP(e)
ELSE EXP(e)
END as Result
FROM cte
答案 1 :(得分:6)
您可以使用以下数学技巧将一系列乘法转换为一系列加法:
exp(log(a) + log(b)) = a * b
所以MUL(a)
是EXP(SUM(LOG(a)))
。
SELECT SUM(val) AS [Sum], EXP(SUM(LOG(val))) AS Product
FROM (VALUES
(1), (2), (3), (4)
) x(val)
这会发出sum = 10, product = 24
。
潜在的问题是四舍五入错误和零因素。
现在,您可以使用常用方法之一来实现正在运行的聚合,例如窗口函数。这是一个已解决的问题。
答案 2 :(得分:4)
为了完整起见,这里是SQL Server 2012的完整解决方案,它使用@usr在另一个答案中建议的EXP(SUM(LOG(val)))
技巧。
WITH
CTE
AS
(
SELECT
0 AS ID
,item
,baseline AS wac_inc
,baseline AS m
FROM baseline
UNION ALL
SELECT
ID
,item
,wac_inc
,1 + wac_inc/100 AS m
FROM wac
)
SELECT
ID
,item
,wac_inc
,m
,EXP(SUM(LOG(m)) OVER (PARTITION BY item ORDER BY ID ROWS UNBOUNDED PRECEDING)) AS MulRows
FROM CTE;
结果集
ID item wac_inc m MulRows
0 A 10.000000000000000 10.000000000000000 10
1 A 2.310000000000000 1.023100000000000 10.231
2 A 1.100000000000000 1.011000000000000 10.343541
3 A 2.130000000000000 1.021300000000000 10.5638584233
4 A 1.340000000000000 1.013400000000000 10.7054141261722
如果SQL Server 2012可用,则此窗口SUM
非常有效。对于以前的版本,任何基于集合的解决方案都会导致O(n*n)
复杂性,这意味着光标将是更好的方式。以下是Aaron Bertrand撰写的一篇非常好的文章,比较了计算总跑数的不同方法:http://sqlperformance.com/2012/07/t-sql-queries/running-totals
或者问题:Calculate running total / running balance
当然,如果您的表很小,那么由于光标开销导致具有O(n*n)
复杂度的基于集合的解决方案可能比O(n)
解决方案运行得更快,因此您需要检查性能你的真实数据。
答案 3 :(得分:2)
你可以使用递归cte:
轻松完成with rec(id ,wi,i,r) as
(
select top (1) w.ID,w.wac_inc,w.item, b.baseline * (1 + (w.wac_inc/100))
from wac w join baseline b on w.item=b.item
union all
select w.ID,w.wac_inc,w.item, r.r * (1 + (w.wac_inc/100))
from wac w
join rec r on (w.ID)-1 = r.id
)
select * from rec
输出:
1 2.31 A 10.231
2 1.1 A 10.343541
3 2.13 A 10.563858
4 1.34 A 10.705414
签入the demo
编辑 - 添加其他解决方案:
你可以从原始表格的副本中获取帮助:
假设您的架构和数据是:
create table wac
(ID int,wac_inc numeric(38,15),item char )
insert wac
values (1,2.31,'A'),
(2,1.1,'A'),
(3,2.13,'A'),
(4,1.34,'A')
1.从原始表中获取副本(使用temp table
或 table variable
)并更新基线表中的第一条记录:
create table #tmp (ID int,wac_inc numeric(38,15),item char, Running_Mul numeric(38,15))
insert into #tmp select id,wac_inc,item,null from wac
update #tmp set Running_Mul = (select top 1 baseline from baseline)*(1+(wac_inc/100))
where id = (select min(id) from #tmp)
2.声明这些变量:
declare @id int,@rm numeric(38,15)
select @id=min(id) from #tmp
select @rm=Running_Mul from #tmp where id=@id
3.更新副本:
update #tmp
set @rm=Running_Mul= case
when @id <> id then @rm*(1+(wac_inc/100))
else Running_Mul
end,
@id=id
现在您可以检查结果:
select * from #tmp
drop table #tmp
结果:
ID wac_inc item Running_Mul
1 2.310000000000000 A 10.231000000000000
2 1.100000000000000 A 10.343541000000000
3 2.130000000000000 A 10.563858000000000
4 1.340000000000000 A 10.705414000000000