2014-01-09 135 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