2014-01-09 172 views
0

我一直在尋找使用CLR聚合來對一系列數據執行一些複雜的財務計算,但是儘管閱讀了很多關於這個主題的文章和很多的小技巧,我還是不太明白。SQL Server 2008 CLR聚合函數

我的輸入是一系列的日期和價值觀,我希望能夠做到以下幾點:

SELECT dbo.FinancialCalc(amount, date) 
FROM (VALUES 
      (-100000, '11/30/2011'), 
      (-50000, '3/15/2012'), 
      (-2500, '7/18/2012')   
      ) n(amount, date) 

這是到目前爲止我的代碼:

[SqlUserDefinedAggregate(Format.UserDefined, MaxByteSize = 8000, Name = "FinancialCalc", IsInvariantToDuplicates = false, IsInvariantToNulls = true, IsInvariantToOrder = true, IsNullIfEmpty = true)] 
[Serializable] 
[StructLayout(LayoutKind.Sequential)] 
public class FinancialCalc : IBinarySerialize 
{  
    private List<Transaction> transactions; 
    private List<DateTime> dates; 
    private List<Double> values; 

    public void Init() 
    {   
     this.transactions = new List<Transaction>(); 
     this.dates = new List<DateTime>(); 
     this.values = new List<double>(); 
    } 

    public void Accumulate(SqlDouble amount, SqlDateTime date) 
    { 
     this.dates.Add(date.Value); 
     this.values.Add(amount.Value); 
    } 

    public void Merge(FinancialCalc Group) 
    { 
     //is this needed? 
    } 

    public SqlDouble Terminate() 
    { 
     //here is where I would do the calc: return transactions.Calculate() or somethine 
     return values.Sum(); 
    } 

    public void Read(System.IO.BinaryReader r) 
    { 
     int itemCount = r.ReadInt16(); 

     for (int i = 0; i <= itemCount - 1; i++) 
     { 
      this.values.Add(r.ReadDouble());   
     } 
    } 

    public void Write(System.IO.BinaryWriter w) 
    { 
     w.Write(this.values.Count); 
     foreach (double s in this.values) 
     { 
      w.Write(s); 
     } 
    } 
} 

如何順利拿到將SQL查詢中的數據放入List<Transaction>以便我可以處理它並返回計算的值?

+0

你需要日期和值的列表,或者就是你到目前爲止所擁有的?你想要的只是一個(任意)'Transaction'對象列表嗎? 「Transaction」對象是什麼樣的? –

+0

那些其他列表僅供測試 - 交易是我所需要的。交易看起來像'公共類交易 { 公共double值{獲得;設置;} 公衆的DateTime日期{獲取;集;}} ' 所以 – woggles

+0

,類似於我的回答假設的一個。所有需要改變的地方(如果你不想改變你的'Transaction'定義以符合我的要求)就是使用對象初始值設置語法,而不是我在「Accumulate」和「Read」中設想的構造函數。 –

回答

1

如果我假設Transaction看起來像這樣:

public class Transaction 
{ 
    private readonly double _amount; 
    private readonly DateTime _date; 
    public Transaction(double amount,DateTime date){ 
    _amount = amount; 
    _date = date; 
    } 
    public double Amount {get{return _amount;}} 
    public DateTime Date {get{return _date;}} 
} 

那麼,我想那是你真正想要看起來是這樣的:

[SqlUserDefinedAggregate(Format.UserDefined, 
     //Play it safe, we don't know how large we'll get 
     MaxByteSize = -1, 
     Name = "FinancialCalc", IsInvariantToDuplicates = false, 
     IsInvariantToNulls = true, IsInvariantToOrder = true, 
     IsNullIfEmpty = true)] 
[Serializable] 
[StructLayout(LayoutKind.Sequential)] 
public class FinancialCalc : IBinarySerialize 
{  
    private List<Transaction> transactions; 

    public void Init() 
    {   
     this.transactions = new List<Transaction>(); 
    } 

    public void Accumulate(SqlDouble amount, SqlDateTime date) 
    { 
     this.transactions.Add(new Transaction(date.Value,amount.Value)); 
    } 

    public void Merge(FinancialCalc Group) 
    { 
     //Yes, you do need this. Group contains another set of transactions 
     //and is going to disappear after this method has been called 
     this.transactions.AddRange(Group.transactions); 
    } 

    public SqlDouble Terminate() 
    { 
     //Do your calculation based on the content of transactions 
     return new SqlDouble(transactions.Sum(t=>t.Amount)); 
    } 

    public void Read(System.IO.BinaryReader r) 
    { 
     int itemCount = r.ReadInt16(); 

     for (int i = 0; i <= itemCount - 1; i++) 
     { 
      this.transactions.Add(new Transaction(r.ReadDouble(), 
             new DateTime(r.ReadInt64()));   
     } 
    } 

    public void Write(System.IO.BinaryWriter w) 
    { 
     w.Write(this.transactions.Count); 
     foreach (var t in this.transactions) 
     { 
      w.Write(t.Amount); 
      w.Write(t.Date.ToBinary()); 
     } 
    } 
} 
+0

謝謝@Damien,非常感謝。雖然在部署時出現錯誤 - 由於字段'CS $ <> 9__CachedAnonymousMethodDelegate1','CREATE AGGREGATE失敗,因爲'FinancialCalc'類型不符合UDAGG規範''我假設這是因爲終止方法中的LINQ,反正我不需要。 – woggles

+0

@woggles - 是的,我只是手工編寫的,我沒有嘗試編譯它或將它添加到SQL Server實例:-( –

+0

幾乎在那裏:)另一個問題轉換日期我想:System.ArgumentOutOfRangeException :蜱必須介於DateTime.MinValue.Ticks和DateTime.MaxValue.Ticks之間。 參數名稱:在運行sql時使用ticks – woggles