SQL CLR聚合错误地返回null

时间:2013-07-10 14:05:27

标签: sql-server aggregate-functions sqlclr

我已经在我的数据库中添加了一个用户定义的聚合,用于计算组的产品。

代码基本上是从here逐字记录的。

我正在使用该函数计算我有月度回报数据的金融工具的生命周期回报。该表看起来像这样:

----------------------------------------------------------
| InstrumentId(int) | MonthEnd(datetime) | Return(float) |
----------------------------------------------------------

我的查询如下:

SELECT R1.InstrumentId,
       R1.MonthEnd,
       R1.MonthlyReturn,
       dbo.Product(1 + R2.MonthlyReturn) AS TotalReturn
FROM Returns R1
INNER JOIN Returns R2 ON  R2.InstrumentId = R1.InstrumentId 
                      AND R2.MonthEnd <= R1.MonthEnd
WHERE R1.InstrumentId BETWEEN 1 AND 50
GROUP BY R1.InstrumentId, R1.MonthEnd, R1.MonthlyReturn
ORDER BY R1.InstrumentId, R1.MonthEnd

当我只有少量乐器时,查询工作正常,但添加某些乐器会导致每个结果都为NULL。当我用OPTION(MAXDOP 1)执行查询时,结果很好。

有谁知道造成这个问题的原因是什么?

编辑: 忘记提到我正在运行SQL Server 2012并且聚合目标是.NET 4.5

2 个答案:

答案 0 :(得分:1)

如果我希望它忽略NULL,我会对Product aggregate进行修改。

更改属性:

[Microsoft.SqlServer.Server.SqlUserDefinedAggregate(
   Microsoft.SqlServer.Server.Format.Native,
   IsInvariantToDuplicates = false,
   IsInvariantToNulls = true,      // receiving a NULL value will be ignored
   IsInvariantToOrder = true,
   IsNullIfEmpty = true,
   Name = "Product"
)]

更改Accumulate

 public void Accumulate(System.Data.SqlTypes.SqlDouble number) {
  if (!this.HasValue && !number.IsNull) { //Don't know if we'll be passed a NULL, but protect ourselves nonetheless
     this.Result = number;
  } else if (number.IsNull) {
     return; //Avoid setting HasValue
  } else {
     this.Result = System.Data.SqlTypes.SqlDouble.Multiply(this.Result, number);
  }
  this.HasValue = true;
}

更改Merge

public void Merge(Product group) {
  if (group.HasValue) {
    if(this.HasValue) {
     this.Result = System.Data.SqlTypes.SqlDouble.Multiply
            (this.Result, group.Result);
    } else { //We may never have had our own value set
     this.Result = group.Result;
     this.HasValue = true;
    }
  }
}

我不确定是否确实需要对Merge进行更改,但出于安全考虑,我会这样做。

答案 1 :(得分:0)

如果1 + R2.MonthlyReturn为肯定,我会考虑使用等效的exp(sum(log(...)))

SELECT R1.InstrumentId,
       R1.MonthEnd,
       R1.MonthlyReturn,
       EXP(SUM(LOG(1 + R2.MonthlyReturn))) AS TotalReturn
FROM Returns R1
INNER JOIN Returns R2 ON  R2.InstrumentId = R1.InstrumentId 
                      AND R2.MonthEnd <= R1.MonthEnd
WHERE R1.InstrumentId BETWEEN 1 AND 50
GROUP BY R1.InstrumentId, R1.MonthEnd, R1.MonthlyReturn
ORDER BY R1.InstrumentId, R1.MonthEnd