UDA生成错误,缓冲区大小不足

时间:2010-11-22 22:14:14

标签: sql sql-server database sql-server-2005

我在SQL 2005中有一个UDA,它一直生成以下错误。我猜这很可能是由于最大字节大小为8000的限制....有什么工作我可以用来解决这个问题吗?有关在2005年避免此限制的任何建议吗?我知道2008年假设已经取消了这些限制,但暂时无法升级。

A .NET Framework error occurred during execution of user-defined routine or aggregate "CommaListConcatenate": 
System.Data.SqlTypes.SqlTypeException: The buffer is insufficient. Read or write operation failed.
System.Data.SqlTypes.SqlTypeException: 
   at System.Data.SqlTypes.SqlBytes.Write(Int64 offset, Byte[] buffer, Int32 offsetInBuffer, Int32 count)
   at System.Data.SqlTypes.StreamOnSqlBytes.Write(Byte[] buffer, Int32 offset, Int32 count)
   at System.IO.BinaryWriter.Write(String value)
   at TASQLCLR.CommaListConcatenate.Write(BinaryWriter w)

2 个答案:

答案 0 :(得分:5)

对于SQL 2005,您可以通过使用分隔符将一个参数转换为多个参数来解决8000字节限制。我不需要自己详细介绍细节,但你可以在这里找到答案:http://www.mssqltips.com/tip.asp?tip=2043

对于SQL 2008,您需要将MaxByteSize作为-1传递。如果您尝试传递大于8000的数字,SQL将不允许您创建聚合,抱怨存在8000字节限制。如果你传入-1,它似乎解决了这个问题,并允许你创建聚合(我也测试了> 8000字节)。

错误是:

  

大小(100000)   “Class.Concatenate”不在   有效范围。大小必须为-1或a   数字在1到8000之间。

这是VB.NET支持的工作类定义> SQL 2008中的8000字节。

<Serializable(), SqlUserDefinedAggregate(Format.UserDefined, 
IsInvariantToNulls:=True, 
IsInvariantToDuplicates:=False, 
IsInvariantToOrder:=False, MaxByteSize:=-1)>
<System.Runtime.InteropServices.ComVisible(False)> _
Public Class Concatenate Implements IBinarySerialize
End Class

答案 1 :(得分:3)

下面的代码显示了如何计算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的转换过多,反之亦然,在处理数百万行时会变慢,无论如何,我很乐意尝试它。对于其他用途,这是一个很好的模式。