下面的代码显示了如何计算 SQLAggregate 中一组十进制数的媒体。它解决了实现数据字典的大小参数限制问题。这个想法来自 Expert SQL Express 2005。
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.Linq.Expressions;
using SafeDictionary;
[Microsoft.SqlServer.Server.SqlUserDefinedAggregate(
Format.UserDefined, MaxByteSize=16)]
public struct CMedian2 : IBinarySerialize
{
readonly static SafeDictionary<Guid , List<String>> theLists = new SafeDictionary<Guid , List<String>>();
private List<String> theStrings;
//Make sure to use SqlChars if you use
//VS deployment!
public SqlString Terminate()
{
List<Decimal> ld = new List<Decimal>();
foreach(String s in theStrings){
ld.Add(Convert.ToDecimal(s));
}
Decimal median;
Decimal tmp;
int halfIndex;
int numberCount;
ld.Sort();
Decimal[] allvalues = ld.ToArray();
numberCount = allvalues.Count();
if ((numberCount % 2) == 0)
{
halfIndex = (numberCount) / 2;
tmp = Decimal.Add(allvalues[halfIndex-1], allvalues[halfIndex]);
median = Decimal.Divide(tmp,2);
}
else
{
halfIndex = (numberCount + 1) / 2;
median = allvalues[halfIndex - 1];
tmp = 1;
}
return new SqlString(Convert.ToString(median));
}
public void Init()
{
theStrings = new List<String>();
}
public void Accumulate(SqlString Value)
{
if (!(Value.IsNull))
theStrings.Add(Value.Value);
}
public void Merge(CMedian2 Group)
{
foreach (String theString in Group.theStrings)
this.theStrings.Add(theString);
}
public void Write(System.IO.BinaryWriter w)
{
Guid g = Guid.NewGuid();
try
{
//Add the local collection to the static dictionary
theLists.Add(g, this.theStrings);
//Persist the GUID
w.Write(g.ToByteArray());
}
catch
{
//Try to clean up in case of exception
if (theLists.ContainsKey(g))
theLists.Remove(g);
}
}
public void Read(System.IO.BinaryReader r)
{
//Get the GUID from the stream
Guid g = new Guid(r.ReadBytes(16));
try
{
//Grab the collection of strings
this.theStrings = theLists[g];
}
finally
{
//Clean up
theLists.Remove(g);
}
}
}
您还需要像 Expert 2005 那样实现字典:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace SafeDictionary
{
public class SafeDictionary<K, V>
{
private readonly Dictionary<K, V> dict = new Dictionary<K,V>();
private readonly ReaderWriterLock theLock = new ReaderWriterLock();
public void Add(K key, V value)
{
theLock.AcquireWriterLock(2000);
try
{
dict.Add(key, value);
}
finally
{
theLock.ReleaseLock();
}
}
public V this[K key]
{
get
{
theLock.AcquireReaderLock(2000);
try
{
return (this.dict[key]);
}
finally
{
theLock.ReleaseLock();
}
}
set
{
theLock.AcquireWriterLock(2000);
try
{
dict[key] = value;
}
finally
{
theLock.ReleaseLock();
}
}
}
public bool Remove(K key)
{
theLock.AcquireWriterLock(2000);
try
{
return (dict.Remove(key));
}
finally
{
theLock.ReleaseLock();
}
}
public bool ContainsKey(K key)
{
theLock.AcquireReaderLock(2000);
try
{
return (dict.ContainsKey(key));
}
finally
{
theLock.ReleaseLock();
}
}
}
}
字典必须部署在具有不安全代码授权的单独程序集中。这个想法是通过保存在内存数据结构字典中来避免序列化所有数字。我推荐 Expert SQL 2005 章节:
第 6 章 ■ SQLCLR:架构与
设计注意事项。
顺便说一句,这个解决方案对我不起作用。从 Decimal 到 String 的太多转换,反之亦然,在处理数百万行时会变慢,无论如何,我很喜欢尝试它。对于其他用途,这是一个很好的模式。