Tag Archive for 'VB.Net'

Split Funktion mittels CLR

Eine beliebte Stringfunktion fehler leider nach wie vor im SLQ Server: Split. Das hat sich auch mit dem neuen SQL Server 2008 immer noch nicht geändert. Ich hatte ja schon mal eine Splitfunktion gepostet, möchte dies aber jetzt als CLR Funktion implementieren. Lange Reder kurzer Sinn:

  • Man erstellt im Visual Studio ein neues Datenbankprojekt.
  • In den Projekteinstellungen sollte die Assembly signiert werden (Einstellungen – Signing – Sign the assembly)
  • Unter dem Karteireiter “Database” kann vorerst das Permissionlevel auf Safe bleiben, da wir ja keine externen Resources ansprechen wollen.
    Safe = Zugriff auf Daten des eigenen SQL Servers über den In-process Managed Provider
    External = Zugriff auf externe Resourcen ist erlaubt
    Unsafe = Es kann unmanaged Code ausgeführt werden
    Erhält man eine Fehlermeldung beim Bereitstellen der Assembly, kann es daran liegen das der Datenbank nicht vertraut wird. Dann sollte man die Trustworthy Eigenschaft setzen:

    Alter Database [DB] Set Trustworthy on
    
  • Man fügt dem Projekt eine neue Klasse hinzu, ich habe sie einfach “Stringfunctions” genannt.
  • Dieser Klasse fügen wir eine Prozedur hinzu:
        <SqlProcedure()> _
        Public Shared Sub Split(ByVal Input As SqlString, ByVal Delimiter As SqlString)
            Dim pipe As SqlPipe = SqlContext.Pipe
            Dim splittet() As String = Input.Value.Split(Delimiter.Value.ToCharArray)
            Dim reslist As New List(Of SqlMetaData)
            For i As Integer = 0 To splittet.Length - 1
                reslist.Add(New SqlMetaData("Col" + i.ToString(), SqlDbType.NVarChar, 4000))
            Next
            Dim result As New SqlDataRecord(reslist.ToArray())
            For i As Integer = 0 To splittet.Length - 1
                result.SetString(i, splittet(i))
            Next
            pipe.Send(result)
        End Sub
    

    Das Attribut SqlProcedure() sorgt dafür, das beim Bereitstellen automatisch eine neue Stored Procedure angelegt wird. Als Parameter der Prozedur übergeben wir zwei SQLStrings: Input und Delimiter. Wofür diese stehen sagen die Namen ja schon… ;) Um die Anzahl der notwendigen Spalten in unserer Ergnistabelle zu ermitteln legen wir uns ersteinmal eine neue Liste mit Spaltendefinitionen an. Danach wird unsere Ergebnistabelle erstellt:

    Dim result As New SqlDataRecord(reslist.ToArray())
    

    Jetzt können wir endlich alle Elemente aus dem Array in die Ergebnistabelle schreiben und zurückgeben:

    For i As Integer = 0 To splittet.Length - 1
    	result.SetString(i, splittet(i))
    Next
    pipe.Send(result)
    

    Die Verwendung der Funktion ist denkbar einfach:

    exec Split 'feld1;feld2;feld3;feld4;feld5;feld6;feld7',';'
    

    Und liefert uns eine Tabelle zurück:

     Col0	Col1	Col2	Col3	Col4	Col5	Col6
     feld1	feld2	feld3	feld4	feld5	feld6	feld7
     

Abgeschnittene Strings

Ein anderes interessantes Phänomen lief mir gerade über den Weg: Liest man aus einem SQL Server eine richtig lange Zeichenkette (z.B. mehr als 2000 Zeichen, z.B. mit einer ‘FOR XML …’ Transformation im Select) wird einem das Ergebnis willkürlich bei um die 1000 Zeichen abgeschitten.
Eine generelle Workaroundfunktion ist schnell gebastelt:

Private Shared Function GetLongStringData(ByVal statement As String) As String
	Dim sql As New System.Data.SqlClient.SqlCommand()
	Dim con As New System.Data.SqlClient.SqlConnection()
	con.ConnectionString = "connectionstring"
	sql.CommandText = statement
	sql.Connection = con
	con.Open()
	Dim r As System.Data.SqlClient.SqlDataReader = sql.ExecuteReader()
	Dim strbuilder As New StringBuilder()
	While r.Read()
		strbuilder.Append(r(0))
	End While
	con.Close()
	Return strbuilder.ToString()
End Function

Stellt sich allerdings nach wie vor die Frage warum die Daten abgeschnitten werden. Im SQL Server Management Studio wird alles korrekt angezeigt. Ich vermute das Problem liegt irgendwo bei der Übergabe vom SQL Server in den .Net String. Wenn jemand eine Idee hat, möge er sich bei mir melden. :)