Wofür werden die Interface-Methoden von IBinarySerialize verwendet?

8

Wenn Sie eine benutzerdefinierte Aggregatfunktion erstellen, müssen Sie Folgendes angeben: Aufzählungsformat :

  

Format Enumeration wird von SqlUserDefinedTypeAttribute und verwendet   SqlUserDefinedAggregateAttribute, um das Serialisierungsformat anzugeben   eines benutzerdefinierten Typs (UDT) oder Aggregats.

und wenn UserDefined format verwendet wird, muss Ihre Klasse IBinarySerialize Interface und überschreiben die Methoden read und write .

Meine Frage ist, was genau diese Methoden tun müssen?

Wenn Sie sich die Beispiele ansehen, sollten sie die Aggregation lesen / schreiben können Ergebnis?

Ich versuche beispielsweise, eine SQL CLR-Funktion zu erstellen, die verschiedene Zahlen verkettet. Im T-SQL kann ich 1 bis 255 eindeutige Zahlen (TINYINT-Wert) haben. Ich muss eine Zeichenfolge von ihnen erstellen (mit Begrenzer), aber auch die Zahlen sortieren. Die Funktion scheint zu funktionieren, aber ich bin mir nicht sicher, ob ich die Methoden wie erwartet überschrieben habe:

%Vor%     
gotqn 05.01.2015, 14:47
quelle

4 Antworten

6

Eine bestimmte Instanz des benutzerdefinierten Aggregats (User-Defined Aggregate, UDA) ist nicht garantiert während der gesamten Lebensdauer der Abfrage vorhanden. Es muss eine speicherbare Darstellung haben. Wenn Sie nur Werttypen verwenden (wie im Link "Aufzählungsformat" in der Frage angegeben), verstehen die bereitgestellten Methoden Read und Write , wie Sie die UDA serialisieren und deserialisieren. In diesem Fall würden Sie Format.Native verwenden. Wenn Sie jedoch Referenztypen (String, Sammlungen, benutzerdefinierte Typen usw.) verwenden, müssen Sie festlegen, wie diese Werte serialisiert und deserialisiert werden. In diesem Fall müssen Sie Format.UserDefined verwenden und die Read und Write Methoden, damit Sie diese Operationen steuern können.

Die Werte, die serialisiert werden müssen, sind alles, was benötigt wird, um die neue Instanz der UDA wieder in den Zustand exact zu bringen, in dem sie sich vor der Serialisierung befand. Das bedeutet: Verlassen Sie sich nicht auf die Init () -Methode, die ausgeführt wird (es wird einmal pro Gruppe ausgeführt!), Oder auf Variableninitialisierungen (sie werden einmal pro Instanz ausgeführt, und die UDA kann für mehrere Gruppen wiederverwendet werden, ohne sie neu zu erstellen!). Sie müssen also alle Basiswerte serialisieren, auch wenn sie nicht direkt mit der endgültigen Ausgabe zusammenhängen.

Das heißt, Sie sollten zumindest die in @ Damien_The_Unbelievers Antwort notierten Optimierungen tun:

  • Führen Sie die Sortierung nicht in der Write -Methode durch. Sie tun es bereits in der Terminate -Methode (dem entsprechenden Ort), also ist es sinnlos, zweimal zu machen, ganz zu schweigen von sehr ineffizient.

  • Speichern Sie die Anzahl der Sammlung und dann die einzelnen Elemente

Darüber hinaus:

  • Wenn Sie sagen, dass Ihr UDA "eindeutige Zahlen verkettet", wenn Sie wirklich "verschieden" gemeint haben, dann müssen Sie jede Zahl überprüfen, wenn sie eingeht, um zu sehen, ob sie bereits in der Liste ist. Ich vermute, das ist dein Wunsch, da du IsInvariantToDuplicates auf wahr gesetzt hast. Sie würden dies in der Accumulate -Methode tun:

    %Vor%

    und in der Merge -Methode (aufgerufen, wenn Parallelismus beteiligt ist):

    %Vor%

    Bitte beachten Sie, dass ich Ihre Besetzung - (byte)value - in der Methode Accumulate in die Eigenschaft Value geändert habe. Alle SqlTypes (z. B. SqlByte , SqlString , SqlInt32 usw.) haben eine Eigenschaft Value , die den zu erwartenden .NET-Typ zurückgibt. Dies bedeutet, dass es nicht notwendig ist, ToString() auf einem SqlString aufzurufen, wie es viele Leute tun.

  • Ich wäre vorsichtig mit einem MaxByteSize von 1024. Diese Bedenken würden durch die Implementierung von @ Damiens Vorschlägen teilweise gelindert werden, da das Speichern von "165; 207" in einer Zeichenkette (aktuelle Methode) technisch 14 Bytes (7 Zeichen * 2 Bytes pro Zeichen), während das Speichern der Zählung und der einzelnen Bytes nur 6 Bytes lang ist (4 Bytes für das Int32, um count + 2 einzelne Bytes zu speichern). Und diese Disparität dient nur zum Speichern von 2 Werten. Speichern von 200? Yeesh!

  • Sie haben nicht die IsNullIfEmpty Eigenschaft angegeben. Sie müssen dies angeben, insbesondere in Anbetracht der Tatsache, dass Ihre Methode Terminate eine leere Zeichenfolge zurückgibt, wenn die interne Auflistung null ist. Sie sollten IsNullIfEmpty = false hinzufügen, da Sie NULL nicht zurückgeben möchten, wenn dies nie aufgerufen wird.

  • Die zusätzliche Logik in der Terminate -Methode zur Behandlung einer null -Auflistung ist wahrscheinlich nicht notwendig. Die Sammlung wird in den Methoden Init und Read initialisiert, so dass ich nicht sicher bin, wie es null jemals sein könnte, wenn Terminate aufgerufen wird.

Wenn Sie beispielsweise ein benutzerdefiniertes Aggregat mit Format.UserDefined erstellen möchten, werfen Sie einen Blick auf Optimale Nutzung von SQL Server 2005-UDTs und -UDAs (kostenlose Registrierung erforderlich). Ich schrieb, dass vor dem Herauskommen von SQL Server 2008, das mehr als 8000 Byte serialisieren ließ, Sie (im Moment) die Aspekte bezüglich der Komprimierung der zu serialisierenden Daten ignorieren können.

Wenn Sie mehr über SQLCLR im Allgemeinen erfahren möchten, schreibe ich darüber eine Serie für SQL Server Central: Stairway an SQLCLR (gleiche Seite wie der erste verlinkte Artikel).

    
Solomon Rutzky 13.01.2015, 21:37
quelle
4

Ich würde sagen, dass du mehr Arbeit in deinen Methoden machst, als du brauchst. Alles, was Sie tun müssen, ist genug in die Methode Write zu schreiben, damit Ihre Read -Methode Ihren internen Zustand rekonstruieren kann. Da Ihr interner Zustand nur ein List<byte> ist, müssen Sie nicht alles als Strings behandeln:

%Vor%

Und wie ich in den Kommentaren vorgeschlagen habe, habe ich auch Sort aus der Methode Write entfernt, da es immer einen letzten Sort -Aufruf in Terminate gibt, bevor die Daten erstellt wurden an die Verbraucher Ihres Aggregats weitergegeben.

Wir speichern die Count zuerst in den Daten, so dass wir wissen, wie oft ReadByte in der Methode Read aufgerufen wird. Dies ermöglicht auch eine (wahrscheinlich sinnlose) Optimierung, die wir dem List<byte> -Konstruktor genau sagen können, für wie viele Elemente er Platz benötigt.

    
Damien_The_Unbeliever 13.01.2015 14:55
quelle
3

Ihr Aggregat kann serialisiert und gelöscht werden, während es die Zeilen, auf denen es ausgeführt wird, zur Hälfte durchläuft. Das Datenbankmodul kann dann eine neue Instanz erstellen und deserialisieren, um dorthin zurückzukehren, wo es aufgehört hat.

Daher muss die Methode Write in der Lage sein, den Status des Aggregats zu speichern, wenn nur einige der Datensätze an Accumulate übergeben wurden. Die Methode Read muss in der Lage sein, das Aggregat für weitere Aufrufe in Accumulate oder Merge wieder bereit zu machen.

Als solches würde ich sagen, dass Sie diese korrekt implementiert haben.

    
Martin Brown 13.01.2015 14:44
quelle
1

Die Methoden IBianarySerialize dienen zum Speichern Ihres Objekts und Wiederherstellen für den Fall, dass es auf die Festplatte geschrieben werden muss.

Daher sollte die Methode Write funktionieren Speichern Sie alles, was benötigt wird, um das Objekt in seinem aktuellen Zustand (den Daten) und dem Read sollte die Ausgabe von Write übernehmen und den Status des Objekts festlegen (damit es mit dem Original übereinstimmt).

Die anderen Antworten scheinen ziemlich gut darin zu sein, die Probleme mit diesem Prozess anzugehen, obwohl ich empfehle, die Daten, die Sie lesen / schreiben, mit diesen Methoden so klein und schnell wie möglich zu halten.

    
Trisped 13.01.2015 19:45
quelle

Tags und Links