我有一个这样的数据表:
Group ID M0 M1 ... M20
------------------------------------
1 1 10 -2 ... 54
1 2 -8 10 ... -20
2 6 -10 12 ... 0
2 4 45 52 ... 0
2 5 12 102 ... 0
我按照这样分组我的数据:
SELECT Group
, round(sum(M0+M1+M2+...+M20)/count(ID),2) as profit
, round(sum(M0),2) as M0
, round(sum(M1),2) as M1
, ...
, round(sum(M20),2) as M20
FROM
data_table
GROUP BY
Group
我需要添加的是基于M0到M20列的IRR。
我找到了一个看起来像这样的IRR函数:
CREATE FUNCTION [dbo].[ufn_IRR]
(
@strIDs VARCHAR(8000),
@guess DECIMAL(30,10)
)
RETURNS DECIMAL(30, 10)
AS
BEGIN
DECLARE @t_IDs TABLE (
id INT IDENTITY(0, 1),
value DECIMAL(30, 10)
)
DECLARE @strID VARCHAR(12),@sepPos INT,@NPV DECIMAL(30, 10)
SET @strIDs = COALESCE(@strIDs + ',', '')
SET @sepPos = CHARINDEX(',', @strIDs)
WHILE @sepPos > 0
BEGIN
SET @strID = LEFT(@strIDs, @sepPos - 1)
INSERT INTO @t_IDs ( value ) SELECT ( CAST(@strID AS DECIMAL(20, 10)) ) WHERE ISNUMERIC(@strID) = 1
SET @strIDs = RIGHT(@strIDs, DATALENGTH(@strIDs) - @sepPos)
SET @sepPos = CHARINDEX(',', @strIDs)
END
SET @guess = CASE WHEN ISNULL(@guess, 0) <= 0 THEN 0.00001 ELSE @guess END
SELECT @NPV = SUM(value / POWER(1 + @guess, id)) FROM @t_IDs
WHILE @NPV > 0
BEGIN
SET @guess = @guess + 0.00001
SELECT @NPV = SUM(value / POWER(1 + @guess, id)) FROM @t_IDs
END
RETURN @guess
END
示例用法如下:
SELECT dbo.ufn_IRR('-4000,1200,1410,1875,1050',0.00001)
但在我的情况下,我想避免在我的列中创建字符串,如下所示:
SELECT dbo.ufn_IRR(
cast(round(sum([M0]), 2) AS NVARCHAR)+','
+cast(round(sum([M1]), 2) AS NVARCHAR)+','
+cast(round(sum([M2]), 2) AS NVARCHAR)+','
+cast(round(sum([M3]), 2) AS NVARCHAR)+','
+cast(round(sum([M4]), 2) AS NVARCHAR)+','
+...+','
+cast(round(sum([M20]), 2) AS NVARCHAR)
,0.00001)
我认为首先创建一个字符串然后在函数内部将其切换成表格是没有意义的 我想修改IRR功能,因为我可以通过我的列,但我没有想法我应该怎么做:/
答案 0 :(得分:3)
我已更新代码以更改猜测方法 从增量(0.00001) 对于客人0.1至0.01至0.001
该函数的示例用法是
select [dbo].[fn_IRR]('-21000,0,0,1948.00,1948.00,1948.00,1948.00,1948.00,1948.00,1948.00,1948.00,1948.00,1948.00,1948.00,1948.00',0.0000000001)
希望这有帮助。
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
Create 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
答案 1 :(得分:1)
CREATE FUNCTION [dbo].[ufn_IRR]
(
@s0 DECIMAL(30,10),
@s1 DECIMAL(30,10),
@s2 DECIMAL(30,10),
@s3 DECIMAL(30,10),
@s4 DECIMAL(30,10),
@s5 DECIMAL(30,10),
@s6 DECIMAL(30,10),
@s7 DECIMAL(30,10),
@s8 DECIMAL(30,10),
@s9 DECIMAL(30,10),
@s10 DECIMAL(30,10),
@s11 DECIMAL(30,10),
@s12 DECIMAL(30,10),
@s13 DECIMAL(30,10),
@s14 DECIMAL(30,10),
@s15 DECIMAL(30,10),
@s16 DECIMAL(30,10),
@s17 DECIMAL(30,10),
@s18 DECIMAL(30,10),
@s19 DECIMAL(30,10),
@s20 DECIMAL(30,10),
@guess DECIMAL(30,10)
)
RETURNS DECIMAL(30, 10)
AS
BEGIN
DECLARE @t_IDs TABLE (
id INT IDENTITY(0, 1),
value DECIMAL(30, 10)
)
Declare @NPV DECIMAL(30, 10)
INSERT INTO @t_IDs (value) values (@s0);
INSERT INTO @t_IDs (value) values (@s1);
INSERT INTO @t_IDs (value) values (@s2);
INSERT INTO @t_IDs (value) values (@s3);
INSERT INTO @t_IDs (value) values (@s4);
INSERT INTO @t_IDs (value) values (@s5);
INSERT INTO @t_IDs (value) values (@s6);
INSERT INTO @t_IDs (value) values (@s7);
INSERT INTO @t_IDs (value) values (@s8);
INSERT INTO @t_IDs (value) values (@s9);
INSERT INTO @t_IDs (value) values (@s10);
INSERT INTO @t_IDs (value) values (@s11);
INSERT INTO @t_IDs (value) values (@s12);
INSERT INTO @t_IDs (value) values (@s13);
INSERT INTO @t_IDs (value) values (@s14);
INSERT INTO @t_IDs (value) values (@s15);
INSERT INTO @t_IDs (value) values (@s16);
INSERT INTO @t_IDs (value) values (@s17);
INSERT INTO @t_IDs (value) values (@s18);
INSERT INTO @t_IDs (value) values (@s19);
INSERT INTO @t_IDs (value) values (@s20);
SET @guess = CASE WHEN ISNULL(@guess, 0) <= 0 THEN 0.00001 ELSE @guess END
SELECT @NPV = SUM(value / POWER(1 + @guess, id)) FROM @t_IDs
WHILE @NPV > 0
BEGIN
SET @guess = @guess + 0.00001
SELECT @NPV = SUM(value / POWER(1 + @guess, id)) FROM @t_IDs
END
RETURN @guess
END
并使用它
dbo.ufn_IRR(round(sum([M0]), 2),
round(sum([M1]), 2),
.....
round(sum([M20]),2),
0.00001)
答案 2 :(得分:1)
我建议的方法是......
定义一个独立的table @t_IDs ( term INT, value DECIMAL )
。在调用ufn_IRR()
之前,请使用table
rows
手动填充(1, M0), (2, M1) ... (n, M20)
。 (错误,我的SQL
知识这似乎是一个循环步骤,但也许SQL-Server
可以在一个语句中执行此操作?)然后只需使用ufn_IRR()
参数调用@guess
即可。您首先需要修改ufn_IRR()
并丢弃将@strIDs
拆分为@t_IDs table
的初始位。
这种方法具有处理不同大小数据的优势。
ufn_IRR()
使用线性根寻找方法。对于大型或重复性任务,也许可以调查其他方法;关于“内部收益率”的维基百科页面提供了一些建议。
答案 3 :(得分:1)
勾勒出一种方法来做到这一点。
首先,我会编写一个程序来处理这一个表,并执行以下步骤:
select... unpivot...
查询从基表填充它(将21列转换为1列21行数据集)下行是,您只能处理一个表,并且您必须在unpivot查询中硬编码21列的名称,这不是很灵活。
Fancier步骤是使用create #temp表,然后使用动态sql根据编程方式需要处理的任何列(来自任何表)来构建unpivot。
要将其转换为函数,您必须创建一个表变量并将其用作参数。然后,任何想要调用该函数的例程都必须构建并填充#temp表,然后将其传递给函数。
就像我说的那样,它变得相当复杂。为了快速完成工作,我将使用@ valexhome的答案,并担心在需要时更新/升级它。