【问题标题】:SQLCLR custom aggregate with multiple sql_variant parameters具有多个 sql_variant 参数的 SQLCLR 自定义聚合
【发布时间】:2015-11-22 21:30:47
【问题描述】:

喂,

几个月前我在post 上发布了一个关于 CLR 用户定义聚合的问题。

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

和我之前的帖子一样,这两个函数是 sMax 和 sMin,并且会根据第二个返回第一个值。

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

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

【问题讨论】:

    标签: c# sql-server .net-assembly sqlclr user-defined-aggregate


    【解决方案1】:

    使用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作为入参类型。

    【讨论】:

    • 类型映射很奇怪,如果SQL_VARIANT为SMALLINT,则映射到SqlInt16,但如果是DATE,则映射到System.DateTime,而不是SqlDateTime。
    • @Oleksandr 正确。这是因为 DATETIME 数据类型的范围更有限,只能追溯到 1753,它与 SqlDateTime 匹配。但是DATETIME2DATE 都可以追溯到0001-01-01,因此它们都映射到.NET 中的DateTime,因为这是等效的。
    【解决方案2】:

    感谢您的回复。我用这个理念来完成我的聚合函数。

    到目前为止,它正在工作,但并非所有人都认为很好......

    • isNull 不起作用
    • 没有isNull,使用Convert.ToString不好,它用空字符串替换空值。但如果没有它,空值就会崩溃
    • 在 Read 函数中:使用 ReadString 函数。 ReadBytes 更好?
    • 使用 Convert 和 toString 执行 CompareTo 是否是好方法?

    代码:

    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; }
    }
    

    【讨论】:

      猜你喜欢
      • 2015-09-25
      • 1970-01-01
      • 1970-01-01
      • 2019-06-08
      • 2016-07-24
      • 1970-01-01
      • 1970-01-01
      • 2020-02-02
      • 2015-11-10
      相关资源
      最近更新 更多