CLRSQL聚合函数。 LINQ代码在CLR函数内工作,但不能在Aggregate中部署

时间:2011-03-22 14:06:51

标签: .net sql linq aggregate sqlclr

感谢您阅读此内容,

VS2010针对SQLServer2008企业,开发CLR Aggregate函数来计算MODE,该函数返回此错误:

  

“第1行CREATE AGGREGATE失败   因为类型'CMode'不符合   因字段而导致UDAGG规范   'CS $<> 9__CachedAnonymousMethodDelegate1'“

此处产生错误:

int mode = list.GroupBy(n => n).
               OrderByDescending(g => g.Count()).
               Select(g => g.Key).FirstOrDefault();

这是完整的代码:

using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
[Serializable]
[SqlUserDefinedAggregate(Format.UserDefined , MaxByteSize = 8000)]
public struct CMode : IBinarySerialize
    {
    private List<int> list;
    public void Init()
        {
            this.list = new List<int>();
        }
    public void Accumulate(SqlInt16 Value)
        {
            this.list.Add(Value.Value);
        }
    public void Merge(CMode Group)
        {
            this.list.AddRange(Group.list.ToArray());
        }
    public SqlDecimal Terminate()
        {
        SqlInt16 rtn = new SqlInt16();
        int mode = list.GroupBy(n => n).
               OrderByDescending(g => g.Count()).
               Select(g => g.Key).FirstOrDefault();
        rtn = (SqlInt16)mode;
        return rtn;
        }
    //IBinarySerialize
    public void Read(BinaryReader r)
        {
        int itemCount = r.ReadInt16();
        this.list = new List<int>(itemCount);
        for (int i = 0; i <= itemCount - 1; i++)
            {
            this.list.Add(r.ReadInt16());
            }
        }
    //IBinarySerialize
    public void Write(BinaryWriter w)
        {
        w.Write(this.list.Count);
        foreach (Int16 s in this.list)
            {
            w.Write(s);
            }
        }
    }

任何指导都将不胜感激!!

我能够在SQLCLR函数中运行所希望的代码,以验证我是否拥有所有授权,dll在那里等等。:

û

sing System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Linq.Expressions;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Linq;

public partial class UserDefinedFunctions
    {
    [Microsoft.SqlServer.Server.SqlFunction]
    public static SqlInt16 SQLCLR2008MODE()
        {
        List<int> list;
        list = new List<int>();

        list.Add(7);
        list.Add(1);
        list.Add(2);
        list.Add(2);
        list.Add(3);
        list.Add(3);
        list.Add(4);
        list.Add(4);
        list.Add(5);
        list.Add(5);
        list.Add(6);

        int mode = list.GroupBy(n => n).
            OrderByDescending(g => g.Count()).
            Select(g => g.Key).FirstOrDefault();   

        return (Int16)mode;

        }
    };

期待您的评论。

4 个答案:

答案 0 :(得分:2)

试试这段代码。你的代码不起作用,因为编译器会将你的隐式Func委托重写为已编译的委托(我强烈建议使用反射器来检查你自己的眼睛)。这不会是坏事,因为它主要是出于性能原因(每次调用时都要避免编译)但不幸的是,这会创建一个不可序列化的字段,并且只能与SQL Server不兼容。为避免这种情况,您必须使用表达式并手动编译它们。我的实现只在整个调用(init)中创建一次委托。

总而言之,我强烈建议使用HashSet集合实现模式,使用某种分组或者甚至是SortedHashSet。

[Microsoft.SqlServer.Server.SqlUserDefinedAggregate(Format.UserDefined, MaxByteSize = 8000, IsInvariantToOrder=true, IsNullIfEmpty=true, IsInvariantToNulls=true)]
public struct Mode : IBinarySerialize
{
    public void Init()
    {
        placeholder = new List<int>(10000);
        Expression<Func<int, int>> ass = p => p;
        grouper = ass.Compile();
        Expression<Func<IGrouping<int, int>,int>> ass2 =  q =>  q.Count();
        sorter = ass2.Compile();

    }

    public void Accumulate(SqlInt32 Value)
    {
        placeholder.Add(Value.Value);
    }

    public void Merge(Mode Group)
    {
        placeholder.AddRange(Group.placeholder);
    }

    public SqlInt32 Terminate()
    {
        SqlInt32 result =      placeholder.GroupBy(grouper).OrderByDescending(sorter).FirstOrDefault().Key ?? null;
         placeholder.Clear();
         return result;

    }

    // This is a place-holder member field
    private List<int> placeholder;
    private Func <int, int> grouper;
    private Func<IGrouping<int, int>, int> sorter;

    //IBinarySerialize
    public void Read(BinaryReader r)
    {
        int itemCount = r.ReadInt32();
        this.placeholder = new List<int>(itemCount);
        for (int i = 0; i <= itemCount - 1; i++)
        {
            this.placeholder.Add(r.ReadInt16());
        }
    }
    //IBinarySerialize
    public void Write(BinaryWriter w)
    {
        w.Write(this.placeholder.Count);
        foreach (Int32 s in this.placeholder)
        {
            w.Write(s);
        }
    }


}

答案 1 :(得分:0)

为了计算模式,我决定用SQL编写代码。即使查询数百万行,它也会非常快。查询计算每天的模式。

那是:

select T_Values.Date batch_date, T_Values.value mode
from       (select a.Date, b.value, COUNT(*) num_items
            from T1 a, T2 b
            where a.Batch = b.Batch
            and b.Product_ref = 100
            and b.Attribute_Id = 1052
            group by a.Date, b.value)
           T_Values,
           (select c.Date, MAX(c.num_items) max_num_items
            from
            (
                select a.Date, b.value, COUNT(*) num_items
                    from T1 a, T2 b
                    where a.Batch = b.Batch
                    and b.Product_ref = 100
                    group by a.Date, b.value
            ) c
            group by c.Date
            ) T_Max
where T_Values.num_items = T_Max.max_num_items
      and T_Values.Date = T_Max.Date
order by T_Values.Date

答案 2 :(得分:0)

非常感谢Luckyluke !!!!

我在执行代码时遇到了一些错误,这些错误值与空值有关。在这个过程的最后,我用SQL编写它,它可以非常快速地完成工作。根据您的回答,我尝试实施Mediam。它工作正常,但它有8000参数大小限制。任何有兴趣学习如何绕过该限制的人都可以访问Expert SQL 2005的第6章并实现字典以避免序列化。

  

使用System;       使用System.Data;       使用System.Data.SqlClient;       使用System.Data.SqlTypes;       使用Microsoft.SqlServer.Server;       使用System.Collections.Generic;       使用System.IO;       使用System.Linq;       使用System.Linq.Expressions;

[Serializable]
[SqlUserDefinedAggregate(Format.UserDefined
     

,       MaxByteSize = 8000,       IsInvariantToDuplicates = false,       IsInvariantToOrder = false)]

public struct CMedian :IBinarySerialize
    {

    private List<int> list;
    public void Init()
        {
        this.list = new List<int>();
        }

    public void Accumulate(SqlInt16 Value)
        {
        this.list.Add(Value.Value);
        }

    public void Merge(CMedian Group)
        {
        this.list.AddRange(Group.list.ToArray());
        }

    public static IQueryable<T> ApplyOrdering<T , U>(IQueryable<T>
     

查询,表达式表达式)               {               表达&GT; exp =(表达式&gt;)表达式;               return query.OrderBy(exp);               }

    public SqlDecimal Terminate()
        {
        decimal median;
        int halfIndex;
        int numberCount;

        IQueryable<int> myInts = list.AsQueryable<int>();
        Expression<Func<int , int>> myExpression = i => i;
        var sortedNumbers = (ApplyOrdering<int , int>(myInts ,
     

myExpression));

        numberCount = myInts.Count();
        halfIndex = (numberCount + 1) / 2;

        if ((numberCount % 2) == 0)
            {
            halfIndex = (numberCount) / 2;
            median = ((list.ElementAt(halfIndex) +
     

list.ElementAt(halfIndex + 1))/ 2);                   }               其他                   {                   halfIndex =(numberCount + 1)/ 2;                   median = list.ElementAt(halfIndex);                   }               return(SqlDecimal)中位数;               }           // IBinarySerialize           public void Read(BinaryReader r)               {               int itemCount = r.ReadInt16();               this.list = new List(itemCount);               for(int i = 0; i&lt; = itemCount - 1; i ++)                   {                   this.list.Add(r.ReadInt16());                   }               }           // IBinarySerialize           public void Write(BinaryWriter w)               {               尝试                   {               foreach(Int16 s in this.list)                   {                   w.Write(一个或多个);                   }               }               catch(例外e)                {                抛出新的异常(“Longitud:”+   w.BaseStream.Length +“位置:”+   w.BaseStream.Position);                }

        }
    }

答案 3 :(得分:0)

我使用.NET 3.5在Visual Studio 2010中创建了两个用于计算IRR和NPV的UDA。他们在SQL 2012上运行和部署得很好。但是,当我尝试将它们部署到SQL 2008R2时,它们因可怕的“不符合UDAGG规范”错误而失败。我试图实施LuckyLuke的方法,但没有成功。在我从UDA类中删除所有lambda表达式和LINQ之后,我终于在SQL 2008R2上取得了成功。有趣的是,我仍然可以在我的UDA使用的类中使用这些语言元素。