海蘭,SQLCLR自定義聚合
我有張貼有關CLR用戶定義聚合問題一個月前幾本OON post。
這工作就像一個魅力。但是現在我想用sql_variant類型中的兩個參數完成相同的功能。
就像在我之前的文章中,這兩個函數是sMax和sMin,並且會根據第二個值返回第一個值。
我發現sql_variant類型是C#中的對象類型。但是我很難積累和比較這個對象。
在不知道類型的情況下比較這兩個對象的最佳選擇是什麼?
海蘭,SQLCLR自定義聚合
我有張貼有關CLR用戶定義聚合問題一個月前幾本OON post。
這工作就像一個魅力。但是現在我想用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
作爲輸入參數類型。
感謝您的回覆。我用這個理念來完成我的聚合函數。
到目前爲止,它的工作,但沒有everythinks精...
代碼:
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; }
}