2015-01-05 5 views
8

あなたはenumeration formatを指定する必要があるカスタム集計関数を作成します。IBinarySerializeインターフェイスメソッドとは何ですか?

フォーマットの列挙は、ユーザー定義型(UDT)または集計のシリアル化形式 を示すためにSqlUserDefinedTypeAttributeと SqlUserDefinedAggregateAttributeで使用されています。

UserDefinedフォーマットが使用されている場合、あなたのクラスはIBinarySerialize Interfaceを実装し、そのreadwriteメソッドをオーバーライドする必要があります。

私の質問は、これらの方法が正確に何をする必要がありますか?

examplesを見ると、集計結果を読み書きできるはずですか?

たとえば、別の番号を連結するSQL CLR関数を作成しようとしています。 T-SQLでは、1〜255の異なる数字(TINYINT値)を持つことができます。私は(区切り文字を使用して)それらから文字列を作成する必要があるだけでなく、番号を並べ替えます。機能が動作しているようですが、私は期待通りのメソッドをオーバーライドしている正確にわからない:それは上の動作の行を処理して半分の方法である一方、

[Serializable] 
[ 
    Microsoft.SqlServer.Server.SqlUserDefinedAggregate 
    (
     Microsoft.SqlServer.Server.Format.UserDefined, 
     IsInvariantToNulls = true, 
     IsInvariantToDuplicates = true, 
     IsInvariantToOrder = false, 
     MaxByteSize = 1024 
    ) 
] 
public class ConcatenateAnswersPos : Microsoft.SqlServer.Server.IBinarySerialize 
{ 
    private List<byte> intermediateResult; 

    public void Init() 
    { 
     intermediateResult = new List<byte>(); 
    } 

    public void Accumulate(SqlByte value) 
    { 
     intermediateResult.Add((byte)value); 
    } 

    public void Merge(ConcatenateAnswersPos other) 
    { 
     intermediateResult.AddRange(other.intermediateResult); 
    } 

    public SqlString Terminate() 
    { 
     if (intermediateResult != null) 
     { 
      intermediateResult.Sort(); 
      return new SqlString(string.Join(";", intermediateResult)); 
     } 
     else 
     { 
      return new SqlString(""); 
     } 

    } 

    public void Read(BinaryReader r) 
    { 
     if (r == null) throw new ArgumentNullException("r"); 

     intermediateResult = new List<byte>(); 
     string[] answers = r.ReadString().Split(';'); 

     foreach (string answer in answers) 
     { 
      intermediateResult.Add(Convert.ToByte(answer)); 
     } 
    } 

    public void Write(BinaryWriter w) 
    { 
     if (w == null) throw new ArgumentNullException("w"); 
     intermediateResult.Sort(); 
     w.Write(string.Join(";", intermediateResult)); 
    } 
} 
+1

'Write' *を使って書いたものの全体は、' Read'で読み込み可能でなければなりません(読者はあなただけのものではない基底のストリームに座っています)。 *あなたが置くのは、このインターフェースのクライアント(一般的にはSQL Serverコード)には本当に不透明です。 –

+1

'Write'メソッドから' Sort'を取り出したいと思います。そのデータが呼び出し元に実際に可視になる前に、 'Read'を呼び出して再処理されます(おそらく' Merge'の後に) 'Terminate'を呼び出すと、実結果。 –

答えて

6

ユーザー定義集約(UDA)の特定のインスタンスは、問合せの存続期間全体を通して存在するとは限りません。記憶に残る表現が必要です。質問の列挙形式のリンクに記載されているように、値の種類のみを使用する場合、提供されるReadおよびWriteメソッドは、UDAをシリアル化および逆シリアル化する方法を理解しています。その場合は、Format.Nativeを使用します。しかし、参照型(文字列、コレクション、カスタム型など)を使用するときは、それらの値のシリアル化と逆シリアル化の方法を定義する必要があります。Format.UserDefinedを使用し、ReadWriteメソッドをオーバーライドする必要があります。それらの操作を制御することができます。

シリアル化する必要がある値は、UDAの新しいインスタンスをの正確な状態に戻すために必要なものです。つまり、実行中のInit()メソッド(グループごとに1回実行されます)や変数イニシャライザ(インスタンス化ごとに1回実行され、UDAは再作成せずに複数のグループに再利用できます)に頼らないでください。したがって、たとえそれらが最終出力に直接関係していなくても、すべての基本値を直列化する必要があります。言っ


は、あなたは、非常に少なくとも、@ Damien_The_Unbelieverの答えで述べた最適化を行う必要があります。
  • Write方法で並べ替えをしないでください。あなたはすでにTerminateの方法(適切な場所)でそれをやっているので、非常に非効率的ではないが、2回するのは無駄です。

  • ストアコレクションの数と個々の要素その向こう

  • あなたが本当に「明確な意味ならば、あなたのUDAは「明確な数字を連結します」と言いますあなたがそれがすでにリストに入っているかどうかを確認するために、各番号をチェックする必要があります。 IsInvariantToDuplicatesがtrueに設定されているので、これはあなたの願いです。

    if (!intermediateResult.Contains(value.Value)) 
    { 
        intermediateResult.Add(value.Value); 
    } 
    

    と(並列性が関与しているときに呼び出さ)Merge方法で:あなたは両方Accumulate方法でこれを行うだろうに - - (byte)value

    foreach (byte _NewValue in other.intermediateResult) 
    { 
        if (!intermediateResult.Contains(_NewValue)) 
        { 
        intermediateResult.Add(_NewValue); 
        } 
    }  
    

    私はあなたのキャストを変えていることに注意してくださいValueプロパティを使用してAccumulateメソッドを使用します。 SqlTypes(例:SqlByteSqlStringSqlInt32など)のすべてには、期待する.NETタイプを返すValueプロパティがあります。つまり、SqlStringに多くの人がいるように、ToString()を呼び出す必要はありません。

  • 1024この懸念のMaxByteSizeが部分的に与えられたダミアンの提案@実装することによって軽減されるだろうと私は「165; 207」を保存していることに慎重になり、文字列(現在のメソッド)にすることは技術的には14バイト(7つの文字である* 2 1バイトあたりのバイト数)、カウントおよび個々のバイトの保存はわずか6バイト( + 2個の個別バイトを格納するInt32の場合は4バイト)です。そして、この不一致は2つの値を格納するためのものです。 200を保管していますか?イェー!

  • IsNullIfEmptyプロパティが指定されていません。これを指定する必要があります。特に、内部コレクションがnullの場合は、Terminateメソッドが空の文字列を返すことを考慮してください。これが決して呼び出されない場合NULLを返すことを望まないので、IsNullIfEmpty = falseを追加する必要があります。

  • nullコレクションを処理するためのTerminateメソッドの余分なロジックはおそらく必要ありません。コレクションはInitReadのメソッドで初期化されていますので、Terminateが呼び出されるまでにはどのようにnullになる可能性がありますか分かりません。


あなたは、Format.UserDefinedでユーザー定義集計を作成する例をしたい場合は、(無料の登録が必要です)Getting The Most Out of SQL Server 2005 UDTs and UDAsを見てみましょう。 SQL Server 2008がリリースされる前に、8000バイト以上のシリアル化が可能になると書いています。そのため、シリアル化するデータの圧縮に関する側面を無視することができます。

また、SQLCLRについて一般的に詳しく知りたい場合は、SQL Server Central:Stairway to SQLCLR(最初のリンク先記事と同じサイト)のシリーズを作成しています。

+1

私はあなたの記事をすでに読んで投票しており、残りのシリーズを待っています。だから、 'value'プロパティを使う方が速いのですか?例えば、常に '(byte)value'の代わりに' value.Value'を使うのです。他のヒントもありがとう。次の記事を待っています:-) – gotqn

+0

@gotqnありがとう:) 'Value'を使うのは必ずしも確実ではあるが、確かに一貫性と信頼性が高いのかどうかは不明です。 –

3

をあなたの集計をシリアル化して削除することができます。その後、データベースエンジンは新しいインスタンスを作成し、デシリアライズして中断した場所に戻ることができます。

このように、Writeメソッドは、レコードの一部のみがAccumulateに渡されたときに集約の状態を格納できる必要があります。 Readの方法では、AccumulateまたはMergeにもっと多くのコールを準備して集約バックアップを設定できる必要があります。

このように正しく実装したと言えます。

4

私はあなたが必要以上にあなたの方法でより多くの仕事をしていると言います。あなたが行う必要があるのはWriteメソッドで十分書いて、Readメソッドが内部状態を再構築できるようにすることだけです。あなたの内部状態がちょうどList<byte>あるので、そこに文字列として、すべてを処理する必要はありません:

public void Read(BinaryReader r) 
{ 
    if (r == null) throw new ArgumentNullException("r"); 

    var count= r.ReadInt32(); 

    intermediateResult = new List<byte>(count); 
    for (int i=0;i<count;i++) 
    { 
     intermediateResult.Add(r.ReadByte()); 
    } 
} 

public void Write(BinaryWriter w) 
{ 
    if (w == null) throw new ArgumentNullException("w"); 
    w.Write(intermediateResult.Count); 
    foreach(byte b in intermediateResult) 
    { 
     w.Write(b); 
    } 
} 

そして、私はコメントで示唆したように常にあるだろうから、私もWrite方法からSortを削除しましたが最終的にはSortTerminateに電話してください。作成したデータは、集約のコンシューマーに渡されます。


我々はRead方法でReadByteを呼び出す方法を何回も知っているように、我々は、データ内のCount最初を格納します。これはまた、(おそらく無意味な)最適化を可能にし、List<byte>コンストラクタにスペースが必要なアイテムの量を正確に伝えることができます。

+1

コレクションをよりよく保存する方法に関する優れたキャッチ/リコメンデーション。私はこの答えを私のものと信じました。 +1 –

1

IBianarySerializeのメソッドは、オブジェクトを保存し、ディスクに書き込む必要がある場合に復元する方法です。

このように、現在の状態(データ)でオブジェクトを再作成するのに必要なものはすべて保存する必要があり、Readメソッドはオブジェクトの状態を設定してオブジェクトの状態を設定する必要があります。

他の回答は、これらの方法を使用して読み書きしているデータをできるだけ小さく、速く保つことをお勧めしますが、このプロセスの問題に対処するのにはかなり良いようです。

関連する問題