0

グループの積を計算するために、ユーザー定義の集計をデータベースに追加しました。

コードは本質的に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 であることを忘れていました

4

2 に答える 2

1

これらは、s を無視したい場合に Product 集約に対して行う変更ですNULL

属性を変更します。

[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が本当に必要かどうかはわかりませんが、安全のために変更します。

于 2013-07-11T07:09:16.950 に答える
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
于 2013-07-10T14:36:14.640 に答える