2009-10-09 8 views
6

Ich habe eine Varchar-Spalte in einer Tabelle, die zum Speichern von XML-Daten verwendet wird. Ja, ich weiß, dass es einen XML-Datentyp gibt, den ich verwenden sollte, aber ich denke, dass dies eingerichtet wurde, bevor der XML-Datentyp verfügbar war, so dass ich jetzt einen Varchar verwenden muss. :)SQL Server Xml-Zeichenfolge in Varchar-Feld analysieren

Die gespeicherten Daten sieht ähnlich wie die folgende:

<xml filename="100100_456_484351864768.zip" 
    event_dt="10/5/2009 11:42:52 AM"> 
    <info user="TestUser" /> 
</xml> 

Ich brauche den Dateinamen zu analysieren, die Ziffern zwischen den zwei Unterstrichen zu erhalten, die in diesem Fall wäre „456“. Der erste Teil des Dateinamens "sollte nicht" in der Länge ändern, aber die mittlere Nummer wird. Ich brauche eine Lösung, die funktioniert, wenn sich der erste Teil ändert (Sie wissen, dass sich das ändert, weil "sich nicht ändern sollte" immer bedeutet, dass es sich ändert).

Für das, was ich für jetzt habe, benutze ich XQuery, um den Dateinamen herauszuziehen, weil ich dachte, das ist wahrscheinlich die bessere als gerade String-Manipulation. Ich übergebe die Zeichenkette dafür nach xml, aber ich bin kein XQuery-Experte, daher stoße ich natürlich auf Probleme. Ich habe eine Funktion für XQuery (substring-before) gefunden, konnte sie aber nicht zum Laufen bringen (ich bin mir nicht einmal sicher, ob die Funktion mit SQL Server funktioniert). Es könnte eine XQuery-Funktion geben, um das leicht zu machen, aber wenn es da ist, bin ich mir dessen nicht bewusst.

So, erhalte ich die Dateinamen aus der Tabelle mit einer Abfrage ähnlich den folgenden:

select CAST(parms as xml).query('data(/xml/@filename)') as p 
from Table1 

Daraus Ich gehe davon aus, dass würde ich in der Lage sei, diese wieder in einen String CAST dann einige tun Instring oder Charindex-Funktion, um herauszufinden, wo die Unterstriche sind, so dass ich all das in einer Teilstring-Funktion kapseln kann, um den Teil herauszusuchen, den ich brauche. Ohne zu weit zu gehen, bin ich mir ziemlich sicher, dass ich es auf diese Weise schaffen kann, aber ich weiß, dass es einen einfacheren Weg geben muss. Auf diese Weise würde in der SQL-Anweisung ein großes unlesbares Feld entstehen, das selbst dann, wenn ich es in eine Funktion verschieben würde, immer noch verwirrend sein würde, um herauszufinden, was vor sich geht.

Ich bin mir sicher, es gibt ein einfacher als das, da es scheint, einfache String-Manipulation. Vielleicht kann mich jemand in die richtige Richtung weisen. Danke

+1

Welche Version von SQL Server? –

+0

Entschuldigung, ich habe diesen Kommentar erst jetzt gesehen. Wir verwenden jetzt SQL Server 2008. – Dusty

Antwort

5

Sie können für diese XQuery verwenden - nur ändern Ihre Aussage zu:

SELECT 
    CAST(parms as xml).value('(/xml/@filename)[1]', 'varchar(260)') as p 
FROM 
    dbo.Table1 

, dass Sie eine VARCHAR gibt (260) lange genug, um einen gültigen Dateinamen und den Pfad zu halten - jetzt Sie einen String haben und arbeiten können, auf sie mit SUBSTRING usw.

Marc

+0

Ich schätze Ihre Antwort, aber ich konnte dies mit der Abfrage in meinem Post mit .Query anstelle von .value bekommen. Ich suchte nach dem besten Weg, um den Dateinamen zu analysieren, sobald ich es bekommen habe. Aber jetzt, da wir uns mit dem Thema beschäftigen, ist die bevorzugte Methode die Verwendung von .query oder .value? – Dusty

+1

'query()' gibt einen vollständigen XDM-Ergebnisbaum als Instanz des XML-Datentyps zurück; 'value()' erfordert, dass Ihre Abfrage nur einen einzelnen XDM-Wert zurückgibt und konvertiert sie in einen bestimmten SQL-Typ. Also gehen Sie im Allgemeinen zum ersten, wenn Sie tatsächlich ein XML-Dokument oder -Fragment oder zumindest einen Knotensatz zurückgeben müssen, und für Letzteres, wenn Sie nur einen einzelnen Wert zurückgeben müssen. –

+0

Danke. Das macht Sinn. Obwohl es Ihnen keine Punkte gibt, habe ich Ihren Kommentar aktualisiert. :) – Dusty

1

Leider ist SQL Server keine konforme XQuery-Implementierung - es ist vielmehr eine ziemlich begrenzte Teilmenge einer Entwurfsversion von XQuery spec. Nicht nur hat es nicht fn:substring-before, es hat auch nicht fn:index-of, es selbst zu tun mit fn:substring, noch fn:string-to-codepoints. Soweit ich das beurteilen kann, sind Sie hier mit SQL beschäftigt.

+0

+1 Danke, ich hatte Angst, dass SQL Server eine begrenzte Teilmenge von XQuery hatte. Sieht so aus, als müsste ich die Teilstring-Funktion in SQL Server verwenden, um es so zu tun, wie ich dachte und wie Steve Kass antwortete. – Dusty

4

Der einfache Weg dazu ist mit SUBSTRING und CHARINDEX. Unter der Annahme, (weise oder nicht), dass der erste Teil des Dateinamens nicht Länge nicht ändert, sondern dass Sie noch XQuery verwenden möchten den Dateinamen zu finden, hier ist eine kurze repro das macht, was Sie wollen:

declare @t table (
    parms varchar(max) 
); 
insert into @t values ('<xml filename="100100_456_484351864768.zip" event_dt="10/5/2009 11:42:52 AM"><info user="TestUser" /></xml>'); 

with T(fName) as (
    select cast(cast(parms as xml).query('data(/xml/@filename)') as varchar(100)) as p 
    from @t 
) 
    select 
    substring(fName,8,charindex('_',fName,8)-8) as myNum 
    from T; 

Es sind raffinierte Lösungen, die andere String-Funktionen wie REPLACE und PARSENAME oder REVERSE verwenden, aber keine ist effizienter oder lesbarer. Eine Möglichkeit, die in Betracht gezogen werden kann, ist das Schreiben einer CLR-Routine, die die Behandlung regulärer Ausdrücke in SQL bringt.

Übrigens, wenn Ihre XML-Datei immer so einfach ist, gibt es keinen besonderen Grund für die Verwendung von XQuery. Hier sind zwei Abfragen, die die gewünschte Zahl extrahieren.Die zweite ist sicherer, wenn Sie die Kontrolle über zusätzlichen Leerraum nicht in Ihrem XML-String oder über die Möglichkeit, dass der erste Teil des Länge des Dateinamen ändern:

select 
    substring(parms,23,charindex('_',parms,23)-23) as myNum 
    from @t; 

    select 
    substring(parms,charindex('_',parms)+1,charindex('_',parms,charindex('_',parms)+1)-charindex('_',parms)-1) as myNum 
    from @t; 
+0

+1 Es sieht so aus, als würde ich tun müssen, was ich dachte, was ich tun müsste, benutze den SQL Server Teilstring, um es auszuwerten. Ich schätze Ihre Antwort und die meiste Arbeit für mich. Ich denke, ich werde eine Funktion machen, die etwas ähnlich wie Ihr erster Beitrag tut, aber in dieser Situation würde das zweite von Ihnen gepostete Code-Beispiel funktionieren, aber ich würde lieber XQuery verwenden, um den Dateinamen vor der String-Manipulation herauszupicken. Nochmals vielen Dank für Ihre Hilfe und ich werde dies als die Antwort markieren. – Dusty