Replace von CHAR(0) in SQL Server varchar-Spalte

by Wolfgang Kluge Juni 30, 2009 18:46

Heute kam es in einem meiner Projekt zu folgendem Fehler

Msg 6841, Level 16, State 1, Line 1
FOR XML konnte die Daten für den 'Tabelle'-Knoten nicht serialisieren, weil ein in XML unzulässiges Zeichen (0x0000) enthalten ist. Um diese Daten mithilfe von FOR XML abzurufen, konvertieren Sie sie in den Datentyp 'binary', 'varbinary' oder 'image', und verwenden Sie die BINARY BASE64-Direktive.

Nach kurzem Suchen bemerkte ich dann, dass nicht die Ausgabe mittels FOR XML fehlerhaft war, sondern in einzelnen Feldern einer Tabelle CHAR(0)-Zeichen enthalten waren, die da nicht hingehörten. Die Tabelle wird von anderer Stelle gefüllt und eigentlich wird dabei der entsprechende Text abgeschnitten, dieses Sonderzeichens in dem Fall wohl aber nicht mehr. Nachdem die Befüllung geändert wurde, mussten die bereits geschriebenen CHAR(0)-Einträge noch geändert werden, doch dass stellte sich als schwerer heraus als zuerst angenommen. Sicher war, dass die Zeichen nur am Ende eines Textes vorkamen. Zum Testen gab es folgende temporäre Tabelle

DECLARE @test table(
    Name varchar(30) NULL COLLATE Latin1_General_CI_AS
)
INSERT INTO @test (Name)
           SELECT 'Hallo Welt 1' + CHAR(0) + CHAR(0) + CHAR(0) + CHAR(0)
 UNION ALL SELECT 'Hallo Welt 2' + CHAR(0)
 UNION ALL SELECT 'Hallo Welt 3'
 UNION ALL SELECT ''
 UNION ALL SELECT CHAR(0)
 UNION ALL SELECT CHAR(0) + CHAR(0)
 UNION ALL SELECT NULL

Das Problem sollte schnell gelöst werden können.

UPDATE  @test
SET     Name = SUBSTRING(Name, 1, CHARINDEX(CHAR(0), Name) - 1)
WHERE   Name LIKE '%' + CHAR(0) + '%'

Sieht einfach aus, macht - zumindest theoretisch - genau dass, was ich will, funktioniert aber nicht :(

vollständigen Artikel anzeigen...

Tags: ,

Microsoft SQL Server

MSDN Blog-Parade : Lieblings-Entwickler-Tools

by Wolfgang Kluge Mai 20, 2009 14:43

Die MSDN Blog-Parade zum Thema Lieblings-Tools erfreut sich doch recht großer Beliebtheit (obs an der Gewinnmöglichkeit liegt, ich weiß es nicht *g*). Ich habe lange überlegt, welches denn meine 3 Lieblings-Tools sind und kam zu folgendem Ergebnis:

  1. ReSharper
    Erleichtert die tägliche Arbeit schon ungemein. Es geht sicherlich auch ohne, aber ich möchte es nicht mehr wirklich missen.
  2. AnkhSVN
    Wobei es eigentlich egal ist, mit welcher Versionskontrolle man arbeitet - Hauptsache man tut es und es ist halbwegs gut in die Entwicklungsumgebung integriert ;)
  3. Browser/Internet/Blogs & Foren
    Ja, der Browser wird seltener genannt, aber ohne Browser (und natürlich ohne Internet und ohne die zahlreichen Blogs, Foren und sonstigen Hilfeseiten) wäre mein Leben als Entwickler durchaus hürdenreicher

Visual Studio und/oder eine andere Entwicklungsumgebung wie #develop hab ich (wie Alex Zeitler in seiner Liste) mal nicht genannt - das gehört einfach zum Basis-Set dazu *g*...

Tags:

Microsoft Visual Studio

URL Rewrite und der SiteMap-Provider (Request.PathInfo)

by Wolfgang Kluge Mai 13, 2009 20:03

Heute wollte ich einer meiner Seiten so umstellen, dass URL-Rewrite verwendet wird. Das ging auch recht schnell (entsprechende Links gesetzt und über Request.PathInfo die Parameter ausgelesen), allerdings stellte sich beim Testen dann heraus, dass ich ein Problem mit dem Menü hatte. Dieses konnte nun nicht mehr die aktuelle Seite markieren... Das Menü wird über eine Sitemap erstellt.

Verkürzt sieht der Code für das Menü so aus:

<asp:SiteMapDataSource ID="MenuSource" runat="server" SiteMapProvider="menu" />
<asp:Menu ID="mnu" runat="server" DataSourceID="MenuSource">

In meiner Sitemap sind die entsprechenden URLs eingetragen. Z.B.

<siteMapNode title="Startseite" url="~/default.aspx" />
<siteMapNode title="Projekte" url="~/projects.aspx"/>
<siteMapNode title="Eigene Projekte" url="~/projects.aspx/own"/>

Wird die Seite "/projects.aspx" aufgerufen, so wird der Eintrag "Projekte" entsprechend gekennzeichnet (mittels CSS-Class "AspNet-Menu-Selected"). Das hat auch noch mit "/projects.aspx?test=1" wunderbar funktioniert. Wird dagegen die Seite "/projects.aspx/test" aufgerufen, wird der entsprechende Eintrag nicht mehr gefunden, da mit Request.RawURL verglichen wird. Und da steht "/projects/test" drin.

Es ist also nur verständlich, dass der Provider den Eintrag nicht findet und dementsprechend kein Menüeintrag als aktiv gekennzeichnet werden kann. Erschwerend kommt hinzu, dass durchaus auch mal ein Treffer dabei sein kann (wie in diesem Beispiel beim Aufruf von "/projects.aspx/own").

Da hilft nur selbst den richtigen Eintrag suchen. Das ist natürlich nur nötig, wenn Request.PathInfo gefüllt ist.

protected override void OnInit( EventArgs e ) {
    base.OnInit( e );
    if( MenuSource.Provider.CurrentNode == null && !string.IsNullOrEmpty( Request.PathInfo ) ) {
        MenuSource.Provider.SiteMapResolve += new SiteMapResolveEventHandler( SiteMap_SiteMapResolve );
    }
}

private SiteMapNode SiteMap_SiteMapResolve( object sender, SiteMapResolveEventArgs e ) {
    SiteMapProvider provider = sender as SiteMapProvider;
    if( provider == null ) return null;
    return provider.FindSiteMapNode( e.Context.Request.AppRelativeCurrentExecutionFilePath );
}

Sobald MenuSource.Provider.CurrentNode == null ist und in Request.PathInfo etwas steht, wird auf das Ereignis SiteMapResolve des Providers reagiert. Dort wird nach einem Eintrag im aktuellen SiteMapProvider gesucht, der nur dem Pfad (ohne PathInfo-Angaben) entspricht.

Wenn man das Menü innerhalb eines Controls hat (wo es hingehört *g*), muss der Code auch nur 1x angegeben werden.

Tags: , ,

ASP.NET

Tanz den => Lambda mit mir...

by Wolfgang Kluge Mai 07, 2009 17:55

Wer sich noch erfolgreich gegen die zugegebenermaßen ungewöhnlich anmutende Schreibweise der Lambda-Ausdrücke in .NET wehrt, dem kann nur gesagt werden: so schlimm wie es aussieht ist's bei weitem nicht! Und einmal verinnerlicht ist's wie Fahrradfahren. Warum man sich daran gewöhnen sollte? Weil man es kann ;)

Allgemein

Lambda(λ)-Ausdrücke gehören irgendwie zu den anonymen Methoden. Allerdings hat man sie nicht umsonst "Ausdruck" und nicht "Methode" genannt. Sie sind also nur ähnlich. Sie können sich aber exakt genauso verhalten wie anonyme Methoden. Dazu müssen sie lediglich einem delegate zugewiesen werden. Grob gesagt sind sie eine Weiterentwicklung der anonymen Methoden.

vollständigen Artikel anzeigen...

Tags:

C#

Die 2 Fragezeichen ?? ... und die null

by Wolfgang Kluge Mai 03, 2009 13:26

Nicht unbekannt - aber dennoch nicht gerade oft verwendet - fristet der c#-Operator ?? sein dasein. Zu Unrecht? Lässt sich doch mit diesem recht einfach testen, ob in einer Objektvariable ein Objekt oder null steht und entsprechend etwas anderes zurückgeben. In dem Beispiel

string test1 = null, test2 = "irgendwas";
Trace.WriteLine( test1 ?? "(null)" );
Trace.WriteLine( test2 ?? "(null)" );

wird - wie man sich bestimmt schon denken kann - zuerst "(null)" und dann "irgendwas" ausgegeben. Oder zu Deutsch: Es wird der linke Operand zurückgegeben, falls dieser nicht null ist, ansonsten wird der rechte Operand zurückgegeben.

Damit wird einem c#-Entwickler eine sehr einfache und leserliche Möglichkeit gegeben, auf null-Werte zu reagieren. Selbstverständlich kann man mit dieser Schreibweise auch "Schindluder" betreiben und den Code so unleserlich wie nur irgend möglich gestalten. Wenn man das aber nicht vorhat, warum dann nicht verwenden?

vollständigen Artikel anzeigen...

Tags:

C#

Partitionierte Views anlegen

by Wolfgang Kluge Mai 02, 2009 12:23

Mit Partitionierung ist ein Aufteilen der Daten anhand bestimmter Kriterien gemeint - vor allem riesige Datenbestände können so einen echten Performancegewinn "erleiden" ;) Man kann dabei auch auf Remote-Tabellen zugreifen(Distributed Partitioned View) - aber dieser Artikel soll nur eine kleine Einführung geben.

Partitionierte Views sind dabei kein wirklicher Ersatz für die partitionierten Tabellen. Man kann sich aber zumindest teilweise helfen.

Eine partitionierte View und deren Tabellen unterliegen einigen Einschränkungen. So muss die Kriteriumsspalte innerhalb des Primary Keys vorkommen (oder selbst der Primary Key sein) und eine Identity-Spalte führt zum Fehler. Alle Tabellen müssen gleiche Primary Keys vorweisen. Das Kriterium muss der jeweiligen Tabelle mittels CHECK-Einschränkung bekannt gegeben werden. Dabei darf die CHECK-Einschränkung nur aus folgenden Operatoren bestehen: AND, OR, BETWEEN, <, <=, =, >= und >. Da gibt es noch ein paar Einschränkungen, aber für den Anfang muss das ausreichen...

vollständigen Artikel anzeigen...

Tags: ,

Microsoft SQL Server

Views mit INSTEAD OF UPDATE-Trigger

by Wolfgang Kluge Mai 01, 2009 18:01

Ich stand vor der Problemstellung, eine Tabelle zu optimieren, da die Tabelle an vielen Stellen verwendet wird aber gleichzeitig dafür zu sorgen, dass die Umstellung "abwärtskompatibel" bleibt.

Die Tabelle ist schon etwas älter, mittlerweile gut gefüllt und es werden einige Daten ständig wiederholt - ich wollte also als erstes mal umstrukturieren. Mein erster Gedanke war, eine View zu verwenden, damit andere Abfragen noch genauso funktionieren wie zuvor. Am Anfang reicht es aus, die "wichtigsten" Abfragen zu optimieren und mit der Zeit könnte man dann langsam alle anderen Abfragen ändern sprich optimieren.

Gesagt, getan. Ich habe also die Tabellen angelegt und eine View nach dem Vorbild der Ursprungstabelle angelegt (und aus Performancegründen eine Indexed View verwendet). Damit auch INSERT-, UPDATE- und DELETE-Anweisungen weiterhin funktionieren, wurden entsprechende INSTEAD OF-Trigger geschrieben...

Doch dann durchkreuzte eine simple aber folgenreiche Fehlermeldung mein ganzes Vorhaben.

Msg 414, Level 16, State 1, Line <line>
UPDATE ist nicht zulässig, weil die Anweisung die "<view name>"-Sicht aktualisiert, die an einer Verknüpfung beteiligt ist und einen INSTEAD OF UPDATE-Trigger aufweist.

bzw.

Msg 414, Level 16, State 1, Line <line>
UPDATE is not allowed because the statement updates view <view name> which participates in a join and has an INSTEAD OF UPDATE trigger

vollständigen Artikel anzeigen...

Tags: ,

Microsoft SQL Server

Globale Stored Procedure im SQL Server

by Wolfgang Kluge April 14, 2009 17:38

Um eine Stored Procedure im SQL Server global (d.h. über mehrere Datenbanken hinweg) verfügbar zu machen, gibt es den nur wenig (bzw. offiziell gar nicht) dokumentierten Befehl sp_ms_marksystemobject. Damit es funktioniert muss der Name der Stored Procedure mit "sp_" beginnen und in der master-Datenbank gespeichert sein! Es gibt keine Fehlermeldung, falls dies nicht so wäre...

Folgende Tabelle soll den Unterschied verdeutlichen. Die Aufgerufene Stored Procedure gibt (wie in dem Beispiel unten) lediglich den aktuellen Datenbanknamen zurück.

SP Aufruf aus AdventureWorks-DB DB_NAME()
in master-Datenbank, aber nicht als System Object markiert
EXEC master.dbo.StoredProcedure
master
EXEC dbo.StoredProcedure
Fehler
in master-Datenbank und als System Object markiert
EXEC master.dbo.StoredProcedure
master
EXEC dbo.StoredProcedure
AdventureWorks

vollständigen Artikel anzeigen...

Tags: ,

Microsoft SQL Server

Große bis riesige Dateien einfach splitten und wieder zusammenführen

by Wolfgang Kluge März 27, 2009 21:10

Das Tool ist zwar nun auch schon etwas älter, aber immer noch ein Post wert (ich habs grad rauskramen müssenWink).

Vor Jahren stand ich (schon mal) vor dem Problem, eine (für damalige Zeit riesige) Datei mit 50GB aufzusplitten und wieder zusammenzusetzen. Mit allen Tools die mir zur Verfügung standen, wollte es jedoch nicht so recht funktionieren (Fehler, Systemstillstand, bis hin zum Blue Screen). Nur zum Trotz (*g*) hab ich dann eine Lösung in VBScript (ja, VBS) geschrieben - und es stellte sich heraus, dass diese Lösung auch noch sehr stabil mit sehr großen Dateien funktionierte (man sagt dem WSH und VBScript nicht gerade hinterher, besonders gut mit Dateien umgehen zu können, aber so schlimm kanns nicht sein).

In der ZIP-Datei (1,77 kb) sind 2 WSH-Scripte enthalten - ksSplitFile.wsf und ksJoinFile.wsf. Der Aufruf ist genauso einfach wie die Scripte und mindestens genauso selbsterklärend wie deren Namen selbst.

Aufteilen einer Datei

ksSplitFile.wsf trennt eine beliebige Datei in kleinere Brocken. Wie groß diese Brocken sind, kann festgelegt werden - voreingestellt sind 15 MB.

ksSplitFile.wsf "filename" [chunksize=15]
  filename ist dabei der Pfad zur Datei und
  chunksize die Größe der Brocken in MB

Sieht man vom Auslesen ab, bleibt die Originaldatei unangetastet ;). Die Einzelteile werden in einen Unterordner "CHUNKED_" + Namen im gleichen Verzeichnis abgelegt - liegt die zu splittende Datei "SplitTest.txt" auf dem Desktop, so wird dort auch ein neuer Ordner mit dem Namen "CHUNKED_SplitTest.txt" angelegt. Ist ein solcher Ordner bereits vorhanden, wird zusätzlich noch eine Zahl hinten angehängt.

Die Größe der Einzelteile hab ich übrigens auf 100MB begrenzt. Die Obergrenze ist mehr oder weniger willkürlich dazugekommen (zwischen 15 und 100 MB wars einfach am "schönsten" - zumindest aus damaliger Perfromance-Sicht). Unter 15 MB lohnt es sich meiner Meinung nach nicht (aber es steht natürlich jedem offen, dies zu ändern).

Zusammenführen zu einer Datei

Zurück geht es ähnlich einfach. Mit der Datei ksJoinFile.wsf werden die Einzelteile wieder zu einem großen Ganzen zusammengefügt.

ksJoinFile.wsf "path"
  path ist der Pfad zum CHUNKED_ - Ordner

Die hierbei erstellte Datei heißt so, wie der Ordner. Gibt es eine solche Datei bereits, wird wieder eine Zahl hinten angehängt.

100 GB brauchen natürlich ihre Zeit, aber es geht (dann würde ich allerdings die "Brocken" größer als 15 MB halten) ;) Ob es eine Obergenze gibt, und wann diese dann erreicht wäre - ich weiß es nicht ;) Eventuell bereichtet es mir einer...

Download (1,77 kb)

Tags: , ,

Tools

Powered by BlogEngine.NET 1.6.1.6
Theme by Mads Kristensen | Modified by Mooglegiant and me ;)