具有多个sql_variant参数的SQLCLR自定义聚合

HY,

几个月前我在这篇文章中发布了一个关于CLR User-Defined Aggregates的问题。

这就像一个魅力。 但是现在我想在sql_variant类型中使用两个参数完全相同的函数。

就像在我上一篇文章中一样,这两个函数将是sMax和sMin,并将根据第二个函数返回第一个值。

我发现sql_variant类型是C#中的对象类型。 但我很难积累和比较对象。

在不知道类型的情况下比较这两个对象的最佳选择是什么?

使用SQL_VARIANT / object ,可以使用GetType()方法确定类型,如下所示:

 [Microsoft.SqlServer.Server.SqlFunction(IsDeterministic = false, IsPrecise = true)] public static SqlBoolean GetIfSmallInt(object SomeValue) { return (SomeValue.GetType() == typeof(SqlInt16)); } 

并使用以下方法测试:

 DECLARE @DateTime DATETIME = GETDATE(); SELECT dbo.GetIfSmallInt(@DateTime); -- 0 DECLARE @SmallInt SMALLINT = 5; SELECT dbo.GetIfSmallInt(@SmallInt); -- 1 

请记住,使用SQL_VARIANT / object会有明显的性能损失。 只在你绝对需要的时候使用它。 如果只需要传入INT / SMALLINT / BIGINT,则使用BIGINT / SqlInt64作为输入参数类型。

感谢您的答复。 我用这个哲学来完成我的聚合函数。

到目前为止,它正在运作,但不是每个人都很好……

  • isNull无法正常工作
  • 没有isNull,使用Convert.ToString并不好,它用空字符串替换空值。 但没有它与空值崩溃
  • 在Read函数中:使用ReadString函数。 ReadBytes更好?
  • 要执行CompareTo,使用Convert和toString,这是一个好方法吗?

代码 :

 using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Data.SqlTypes; using System.Diagnostics.Eventing.Reader; using System.Globalization; using Microsoft.SqlServer.Server; using System.Text; using System.Collections; using System.IO; [Serializable] [SqlUserDefinedAggregate( Format.UserDefined, IsInvariantToOrder = true, IsInvariantToNulls = true, IsInvariantToDuplicates = true, MaxByteSize = -1)] public struct sMax : IBinarySerialize, INullable { #region Helpers private struct MyData { public object Data { get; set; } public InType DataType { get; set; } public object Group { get; set; } public InType GroupType { get; set; } public int CompareTo(MyData other) { if (Group == null) return other.Group == null ? 0 : -1; if (other.Group == null) return 1; if (GroupType == InType.Int) return Convert.ToInt32(Group).CompareTo(Convert.ToInt32(other.Group)); if (GroupType == InType.BigInt) return Convert.ToInt64(Group).CompareTo(Convert.ToInt64(other.Group)); if (GroupType == InType.Double) return Convert.ToDouble(Group).CompareTo(Convert.ToDouble(other.Group)); if (GroupType == InType.Date) return Convert.ToDateTime(Group.ToString()).CompareTo(Convert.ToDateTime(other.Group.ToString())); if (GroupType == InType.String) return Convert.ToString(Group).CompareTo(Convert.ToString(other.Group)); else return 0; } 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 enum InType { String, Int, BigInt, Date, Double, Unknow } private InType GetType(object value) { if (value.GetType() == typeof(SqlInt32)) return InType.Int; else if (value.GetType() == typeof(SqlInt64)) return InType.BigInt; else if (value.GetType() == typeof(SqlString)) return InType.String; else if (value.GetType() == typeof(SqlDateTime)) return InType.Date; else if (value.GetType() == typeof(SqlDouble)) return InType.Double; else return InType.Unknow; } #endregion private MyData _maxItem; public void Init() { _maxItem = default(MyData); this.IsNull = true; } public void Accumulate(object data, object group) { if (data != null && group != null) { var current = new MyData { Data = data, Group = group, DataType = GetType(data), GroupType = GetType(group) }; if (current > _maxItem) { _maxItem = current; } } } public void Merge(sMax other) { if (other._maxItem > _maxItem) { _maxItem = other._maxItem; } } public SqlString Terminate() { return this.IsNull ? SqlString.Null : new SqlString(_maxItem.Data.ToString()); } public void Read(BinaryReader reader) { IsNull = reader.ReadBoolean(); _maxItem.Group = reader.ReadString(); _maxItem.Data = reader.ReadString(); if (_maxItem.Data != null) this.IsNull = false; } public void Write(BinaryWriter writer) { writer.Write(this.IsNull); writer.Write(_maxItem.Group.ToString()); writer.Write(_maxItem.Data.ToString()); } public Boolean IsNull { get; private set; } }