CLRSQL Агрегатная функция. LINQ-код работает в рамках функции CLR, но не может быть развернут в Aggregate - PullRequest
2 голосов
/ 22 марта 2011

Спасибо, что прочитали это,

VS2010 против предприятия SQLServer2008, разрабатывающего функцию агрегирования CLR для вычисления MODE, функция возвращает эту ошибку:

"Строка 1, СОЗДАТЬ СОСТАВ, не удалось потому что тип "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 ]

2 голосов
/ 22 марта 2011

Попробуйте этот код. ВАШ код не работает, потому что компилятор переписывает Ваши неявные делегаты 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);
        }
    }


}
0 голосов
/ 04 ноября 2014

Я создал два UDA для вычисления IRR и NPV в Visual Studio 2010 с использованием .NET 3.5.Они отлично работали и разворачивались на SQL 2012. Однако, когда я попытался развернуть их на SQL 2008R2, они потерпели неудачу с ужасной ошибкой «не соответствует спецификации UDAGG».Я пытался реализовать подход LuckyLuke, но безуспешно.Я наконец добился успеха в SQL 2008R2 только после того, как я удалил все лямбда-выражения и LINQ из классов UDA.Интересно, что я все еще мог использовать эти языковые элементы в классе, используемом в моих UDA.

0 голосов
/ 24 марта 2011

Большое спасибо Luckyluke !!!!

Я получил некоторые ошибки при выполнении вашего кода, что-то относительно значений null В конце процесса я кодировал его с помощью SQL, и он очень быстро выполняет свою работу. Следуя идее вашего ответа, я попытался реализовать Mediam. Он работает нормально, но имеет ограничение размера 8000 параметров. Любой, кто хочет узнать, как преодолеть это ограничение, может перейти к главе 6 Expert SQL 2005 и реализовать словарь, чтобы избежать сериализации.

с использованием системы; используя System.Data; using System.Data.SqlClient; использование System.Data.SqlTypes; использование Microsoft.SqlServer.Server; using System.Collections.Generic; используя System.IO; использование System.Linq; using 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>

запрос, выражение выражения) { Выражение> exp = (Выражение>) выражение; 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 = новый список (itemCount); for (int i = 0; i <= itemCount - 1; i ++) { this.list.Add (r.ReadInt16 ()); } } // IBinarySerialize public void Write (BinaryWriter w) { пытаться { foreach (Int16 s в этом списке) { w.Write (ы); } } поймать (исключение е) { бросить новое исключение («Продольно:» + w.BaseStream.Length + "Позиция:" + w.BaseStream.Position); } </p>

        }
    }
0 голосов
/ 24 марта 2011

Чтобы рассчитать режим, я решил закодировать его в 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
...