SQLCLR多参数自定义聚合

问题描述 投票:0回答:3

我对CLR用户自定义聚合的工作原理有一些不理解。

我必须创建一些具有多个参数的自定义CLR聚合,重点是根据第二个参数来获取第一个参数的值。

例如,我的表中有以下值,我需要最老的员工的值 Name 对于每个 Type:

    Type   |   Name   |   Age   
--------------------------------
Manager    | emp 1    |   35    
Manager    | emp 2    |   42    
Developer  | emp 3    |   36    
Developer  | emp 4    |   45    
Developer  | emp 5    |   22    

所以,我想写一个这样的查询,通过使用我的汇编来获得结果。

Select      Type, dbo.fOldestEmployee(Name, Age) AS [Name]
From        xxx
Group By    Type

这样就可以得到结果。

    Type   |   Name   
----------------------
Manager    | emp 2     
Developer  | emp 4    

它看起来像一个CLR用户定义的聚合体是可能的,但我很难找到这种实现的具体例子。

我创建了一个类来收集数据,但我如何对它们进行排序(或做其他事情)?

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

[Serializable]
[SqlUserDefinedAggregate(
    Format.UserDefined,
    IsInvariantToOrder = false, // order changes the result
    IsInvariantToNulls = false, // nulls change the result
    IsInvariantToDuplicates = false, // duplicates change the result
    MaxByteSize = -1)]
public struct sOlder
{
    private List<MyData> _datas;

    public void Init()
    {
        _datas = new List<MyData>();
    }

    public void Accumulate(SqlString valueField, SqlInt32 ValueInt)
    {
        if (!valueField.IsNull && !ValueInt.IsNull)
        {
            _datas.Add(new MyData
            {
                ValField = valueField.Value,
                ValInt = ValueInt.Value
            });
        }
    }

    public void Merge (sOlder Group)
    {
        _datas.AddRange(Group._datas);
    }

    public SqlString Terminate ()
    {
        //...
    }

    public class MyData
    {
        public String ValField { get; set; }
        public Int32 ValInt { get; set; }
    }
}

有什么好办法吗?

c# sql-server .net-assembly sqlclr user-defined-aggregate
3个回答
1
投票

没有必要存储所有记录的列表--你只需要存储到目前为止你看到的最老记录的详细信息。

类似这样的东西应该可以用。

[Serializable]
[SqlUserDefinedAggregate(
    Format.UserDefined,
    IsInvariantToOrder = true,
    IsInvariantToNulls = true,
    IsInvariantToDuplicates = true,
    MaxByteSize = -1)]
public struct sOlder : IBinarySerialize
{
    private struct MyData
    {
        public string Name { get; set; }
        public int? Age { get; set; }

        public int CompareTo(MyData other)
        {
            if (Age == null) return other.Age == null ? 0 : -1;
            if (other.Age == null) return 1;
            return Age.Value.CompareTo(other.Age.Value);
        }

        public static bool operator <(MyData left, MyData right)
        {
            return left.CompareTo(right) == -1;
        }

        public static bool operator >(MyData left, MyData right)
        {
            return left.CompareTo(right) == 1;
        }
    }

    private MyData _eldestPerson;

    public void Init()
    {
        _eldestPerson = default(MyData);
    }

    public void Accumulate(SqlString name, SqlInt32 age)
    {
        if (!name.IsNull && !age.IsNull)
        {
            var currentPerson = new MyData
            {
                Name = name.Value,
                Age = age.Value
            };

            if (currentPerson > _eldestPerson)
            {
                _eldestPerson = currentPerson;
            }
        }
    }

    public void Merge (sOlder other)
    {
        if (other._eldestPerson > _eldestPerson)
        {
            _eldestPerson = other._eldestPerson;
        }
    }

    public SqlString Terminate()
    {
        return _eldestPerson.Name;
    }

    public void Write(BinaryWriter writer)
    {
        if (_eldestPerson.Age.HasValue)
        {
            writer.Write(true);
            writer.Write(_eldestPerson.Age.Value);
            writer.Write(_eldestPerson.Name);
        }
        else
        {
            writer.Write(false);
        }
    }

    public void Read(BinaryReader reader)
    {
        if (reader.ReadBoolean())
        {
            _eldestPerson.Age = reader.ReadInt32();
            _eldestPerson.Name = reader.ReadString();
        }
        else
        {
            _eldestPerson = default(MyData);
        }
    }
}

0
投票

如果你正在寻找你的特定请求的实现,那么 @Richard 的答案看起来是正确的(尽管,你 可能 还需落实 ReadWrite 使用自定义类型的方法 -- -- Format.UserDefined).

然而,从对该问题的评论来看,这似乎是一个一般性问题,即何时对你正在收集的任何信息进行处理。在这种情况下。

  • 在这种情况下... Accumulate 方法被称为 每行 在某个GROUP中。这是个切入点。

  • 这是个切入点。Merge 方法被称为 当使用并行时. SQL Server使用这个方法来组合来自不同线程的信息。根据你所做的算法类型,这里你可能会:结合当前信息和传入信息,决定保留当前信息或传入信息(就像@Richard的实现中所做的那样),根据新的传入信息重新计算当前信息。

  • 这个 Terminate 方法被称为 在每个特定的GROUP结束时. 这里是你做最后的计算逻辑,然后返回预期结果的地方。

这些信息,以及更多的信息,可以在MSDN页面上找到。对CLR用户定义集合的要求.

© www.soinside.com 2019 - 2024. All rights reserved.