GO
/****** Object: UserDefinedFunction [dbo].[fn_IRR] Script Date: 7/28/2014 11:43:40 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [dbo].[fn_IRR]
(
@str varchar(max),
@precision DECIMAL(30,10)
)
RETURNS DECIMAL(30, 10)
AS
BEGIN
declare @AdjValue decimal(30,10)
,@guess Decimal(30,10)
,@guess_new Decimal(30,10)
Select @AdjValue = 0.1, @guess=0
DECLARE @t_IDs TABLE (
id INT IDENTITY(0, 1),
value DECIMAL(30, 10)
)
Declare @NPV DECIMAL(30, 10)
,@iter_cnt int
INSERT INTO @t_IDs
select * from dbo.fn_SplitString(@str,',')
SET @guess = CASE WHEN ISNULL(@guess, 0) <= 0 THEN 0 ELSE @guess END
SELECT @NPV = SUM(value / POWER(1 + @guess, id)) FROM @t_IDs
WHILE ((@NPV > 0 or @AdjValue > @precision) and (isnull(@iter_cnt,0) < 8192))
BEGIN
SET @guess_new = @guess + @AdjValue
SELECT @NPV = SUM(value / POWER(1 + @guess_new, id)) FROM @t_IDs
set @iter_cnt = isnull(@iter_cnt,0) + 1
if (@NPV > 0)
select @guess=@guess_new
else
select @AdjValue=@AdjValue/10
END
RETURN @guess
END
USE [absmart_v442]
GO
/****** Object: UserDefinedFunction [dbo].[fn_SplitString] Script Date: 7/28/2014 11:47:58 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [dbo].[fn_SplitString](
@str nvarchar(max)
,@sep nvarchar(max)
)
RETURNS TABLE
AS
RETURN
WITH a AS(
SELECT CAST(0 AS BIGINT) as idx1,CHARINDEX(@sep,@str) idx2
UNION ALL
SELECT idx2+1,CHARINDEX(@sep,@str,idx2+1)
FROM a
WHERE idx2>0
)
SELECT SUBSTRING(@str,idx1,COALESCE(NULLIF(idx2,0),LEN(@str)+1)-idx1) as StrValue
FROM a
所以我有这个代码,它计算一组值的IRR。该函数做了很好的工作(我从其他stackoverflow问题得到了代码)。我唯一的问题是如果IRR&gt;我无法找到让它显示负值的方法。如果IRR <0,则该函数给出良好的结果。 0它返回0,但我希望它返回否定结果。
问题在于,我不太了解如何计算IRR值,或者如果IRR是负数,它为什么要打印0。由于一些问题(我已尝试过),我也无法使用SQL中的调试器。任何人都知道如何确保函数始终返回IRR,即使它是负值。
select [dbo].[fn_IRR]('-45000,15000,20000,25000.00,10000.00,5000',0.00000000001) as IRR should return 0.2299339513
select [dbo].[fn_IRR]('-170000,32000,35000,33000.00,29000.00,36000',0.00000000001) as IRR
在Excel中重新开始时应该返回-0.98
答案 0 :(得分:0)
我设法自己找到解决方案,你不能用-0.99来初始化@guess
答案 1 :(得分:0)
上面的原始示例适用于正数,但这不适用于负数。问题与POWER加1有关,因此它在循环中始终为正。我扩展了上述解决方案,适用于正数和负数。
注意:在一个完成的ELSE块中,您必须从1中减去该值,然后转换为负数。
ALTER FUNCTION [dbo].[fn_IRR]
( @str varchar(max), @precision DECIMAL(30,10) ) 返回DECIMAL(30,10) 如 开始 声明@AdjValue十进制(30,10) ,@ guess Decimal(30,10) ,@ guess_new十进制(30,10)
Select @AdjValue = 0.1, @guess=0
DECLARE @t_IDs TABLE (
id INT IDENTITY(0, 1),
value DECIMAL(30, 10)
)
Declare @NPV DECIMAL(30, 10)
,@iter_cnt int
INSERT INTO @t_IDs
select * from dbo.fn_SplitString(@str,',')
SET @guess = CASE WHEN ISNULL(@guess, 0) <= 0 THEN 0 ELSE @guess END
SELECT @NPV = SUM(value / POWER(1 + @guess, id)) FROM @t_IDs
IF @NPV > 0
BEGIN
WHILE ((@NPV > 0 or @AdjValue > @precision) and (isnull(@iter_cnt,0) < 8192))
BEGIN
SET @guess_new = @guess + @AdjValue
SELECT @NPV = SUM(value / POWER(1 + @guess_new, id)) FROM @t_IDs
set @iter_cnt = isnull(@iter_cnt,0) + 1
if (@NPV > 0)
select @guess=@guess_new
else
select @AdjValue=@AdjValue/10
END
END
ELSE
BEGIN
WHILE ((@NPV > 0 or @AdjValue > @precision) and (isnull(@iter_cnt,0) < 8192))
BEGIN
SET @guess_new = @guess + @AdjValue
SELECT @NPV = SUM(value / POWER(@guess_new, id)) FROM @t_IDs
set @iter_cnt = isnull(@iter_cnt,0) + 1
if (@NPV > 0)
select @guess=@guess_new
else
select @AdjValue=@AdjValue/10
END
SET @guess = 1 - @guess
SET @guess = -@guess
END
RETURN @guess
END