3

我有一个这样的数据表:

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 函数,这样我就可以传递我的列,但我不知道该怎么做:/

4

4 回答 4

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
于 2013-09-16T12:38:21.607 回答
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)
于 2012-08-22T14:16:29.850 回答
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()使用线性寻根方法。对于大型或重复性任务,也许可以研究其他方法;关于“内部收益率”的维基百科页面提出了一些建议。

于 2012-08-22T15:00:07.627 回答
1

勾勒出一种方法来做到这一点。

首先,我将编写一个处理这个表的过程,步骤如下:

  • 创建临时表
  • 使用select... unpivot...查询从基表中填充它(将 21 列转换为 1 列 21 行数据集)
  • 对临时表运行数学

缺点是只能处理一张表,而且必须硬编码 unpivot 查询中 21 列的名称,不够灵活。

更高级的步骤是使用创建 #temp 表,然后使用动态 sql 根据您以编程方式确定需要处理的任何列(来自任何表)构建 unpivot。

要将其转换为函数,您必须创建一个表变量并将其用作参数。然后,任何想要调用函数的例程都必须构建并填充#temp 表,然后将其传递给函数。

就像我说的,它变得相当复杂。为了快速完成工作,我会选择@valexhome 的答案,并担心稍后在需要时更新/升级它。

于 2012-08-22T18:40:48.130 回答