2010-11-22 5 views
1

J'ai un UDA dans SQL 2005 qui continue à générer l'erreur ci-dessous. Je suppose que c'est probablement dû aux limitations de la taille maximale de 8000 octets .... Y a-t-il du travail autour de moi que je peux utiliser pour contourner cela? Des suggestions pour éviter cette limitation en 2005? Je sais que 2008 a supposément pris ces limitations, mais je ne peux pas mettre à niveau pour le moment.Erreur de génération UDA, taille de tampon insuffisante

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) 

Répondre

5

Pour SQL 2005 vous pouvez résoudre la limite de 8000 octets en tournant un paramètre dans plusieurs paramètres à l'aide d'un séparateur. Je n'ai pas eu besoin d'entrer dans les détails moi-même, mais vous pouvez trouver la réponse ici: http://www.mssqltips.com/tip.asp?tip=2043

Pour SQL 2008, vous devez passer MaxByteSize comme -1. Si vous essayez de passer un nombre supérieur à 8000, SQL ne vous laissera pas créer l'agrégat, se plaignant qu'il existe une limite de 8000 octets. Si vous passez -1, il semble contourner ce problème et vous permet de créer l'agrégat (que j'ai également testé avec> 8000 octets).

L'erreur est:

La taille (100000) pour "Class.Concatenate" est pas dans la plage valide. La taille doit être -1 ou un nombre entre 1 et 8000.

Voici la définition de la classe ouvrière pour VB.NET pour soutenir> 8000 octets dans SQL 2008.

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

Pourquoi est-ce la réponse? L'article lié à ne précise pas comment contourner la limite de 8k, il donne juste une solution de contournement pour une fonction de concaténation de prendre deux paramètres? – CuppM

3

Le code ci-dessous montre comment calculer le média d'un ensemble de nombres décimaux dans une SQLAggregate. Il résout le problème de la limitation des paramètres de taille mettant en œuvre un dictionnaire de données. L'idée est prise d'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); 
     } 
    } 
} 

vous devez également mettre en œuvre le dictionnaire comme Expert 2005 fait:

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

Le dictionnaire doit être déployé dans un ensemble séparé avec des subventions de code dangereux. L'idée est d'éviter de sérialiser tous les nombres en gardant en mémoire le dictionnaire de structure de données. Je recommande le chapitre SQL Expert 2005:

CHAPITRE 6 ■ SQLCLR: ARCHITECTURE ET CONSIDERATIONS DE CONCEPTION.

Par ailleurs, cette solution n'a pas fonctionné pour moi. Trop de conversions de Decimal en String et viceversa le rendent lent quand on travaille avec des millions de lignes, de toute façon, j'ai aimé l'essayer. Pour d'autres usages c'est un bon motif.

Questions connexes