【问题标题】:UDA generating error, insufficient buffer sizeUDA 生成错误,缓冲区大小不足
【发布时间】:2010-11-22 22:14:14
【问题描述】:

我在 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)

【问题讨论】:

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


    【解决方案1】:

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

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

    错误是:

    大小 (100000) 为 “Class.Concatenate”不在 有效范围。大小必须为 -1 或 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
    

    【讨论】:

    • 为什么是这个答案?链接到的文章没有说明如何绕过 8k 限制,它只是提供了一种解决方法,让连接函数采用两个参数?
    【解决方案2】:

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

    【讨论】:

      猜你喜欢
      • 2016-02-22
      • 1970-01-01
      • 2020-09-05
      • 2018-09-19
      • 2021-09-30
      • 2015-09-24
      • 2012-04-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多