2016-06-16 7 views
2

Ich muss E-Mail Subject aus einer XML als gespeichert extrahieren.Extrahieren von Daten aus XML gespeichert als Nvarchar

Ich bin mit dieser Abfrage:

SELECT rtrim((SELECT CAST(
        SUBSTRING(
         [XML] 
         ,patindex('%<SUBJECT>%', [XML]) 
         ,patindex('%</SUBJECT>%', [XML])-patindex('%<SUBJECT>%', [XML])+len('</SUBJECT>') 
        ) as XML).value('(/SUBJECT/OPTION)[1]','nvarchar(2000)') 
      FROM dbo.Mails 
)) as SUBJECT 

Was diese Abfrage tun, ist zunächst die Zeichenfolge zu extrahieren, die E-Mail-Betreff enthält (<SUBJECT>....</SUBJECT>), die ich in XML konvertieren und dann den Betreff Wert erhalten mit value Funktion XML.

Dies funktioniert gut, aber in einigen Fällen ist das XML nicht gut gebildet und das Parsen schlägt fehl. Beispiel:

DECLARE @XMLData XML = '<SUBJECT> 
     <OPTION CONSTRAINT="MASTER.IN_TITLE = '' OR MASTER.IN_LASTNAME = ''"><![CDATA[Découvrez nos offres de location]]> 
     </OPTION> 
     <OPTION CONSTRAINT="IN_TITLE <> '' AND MASTER.IN_LASTNAME <> ''"><![CDATA[~IN_TITLE~ ~IN_LASTNAME~, découvrez nos offres de location]]> 
     </OPTION> 
    </SUBJECT>' 

select rtrim((@XMLData).value('(/SUBJECT/OPTION)[1]','nvarchar(2000)')) 

Hier im Option Attribut Constraint Ich habe einen besonderen Charakter <, Wenn ich versuche, diesen Charakter zu entkommen sie alle anderen Charakter entkommt und ich verlor die XML-Struktur. Wie kann man dem entkommen?

Ein weiteres Beispiel ist dies:

DECLARE @XMLData XML = '<SUBJECT> 
     <OPTION NAME="DEFAULT"><![CDATA[~(IF((IN_TITLE<>'' AND IN_LASTNAME<>''),IN_TITLE&' '&IN_LASTNAME&',',''))~ nos plus belles réalisations de 2015]]> 
     </OPTION> 
    </SUBJECT>' 

select rtrim((@XMLData).value('(/SUBJECT/OPTION)[1]','nvarchar(2000)')) 

Hier habe ich Fehler in der Nähe von &IN_LASTNAME& aber ich dachte, dass wir Sonderzeichen nicht in CDATA entkommen müssen!

Hat jemand eine Lösung, um diese Fehler beim Parsen zu vermeiden?

Antwort

1

Wie werden diese XML generiert? Ist das unter Ihrer Kontrolle? Die drei Zeichen des Bösen "<,> und &" müssen speziell behandelt werden, entweder CDATA oder entkommen. Wenn das XML richtig generiert wird, sollte es nicht möglich sein, sie in verbotenen Orten zu bekommen ...

Hier sind zwei Arbeitsbeispiele. Die zweite ist identisch mit Rhys Jones ... Im ersten Beispiel ersetze ich "<>" durch &lt;&gt;.

Btw: Da Sie offensichtlich mit anderen Sonderzeichen zu tun haben, sollten Sie Ihre Zeichenfolgen mit N'string' markieren, um es als unicode zu lesen.

DECLARE @XMLData XML = REPLACE(N'<SUBJECT> 
     <OPTION CONSTRAINT="MASTER.IN_TITLE = '' OR MASTER.IN_LASTNAME = ''"><![CDATA[Découvrez nos offres de location]]> 
     </OPTION> 
     <OPTION CONSTRAINT="IN_TITLE <> '' AND MASTER.IN_LASTNAME <> ''"><![CDATA[~IN_TITLE~ ~IN_LASTNAME~, découvrez nos offres de location]]> 
     </OPTION> 
    </SUBJECT>','<>','&lt;&gt;'); 

select rtrim((@XMLData).value('(/SUBJECT/OPTION)[1]','nvarchar(2000)')); 
GO 

DECLARE @XMLData XML = N'<SUBJECT> 
     <OPTION NAME="DEFAULT"><![CDATA[~(IF((IN_TITLE<>'''' AND IN_LASTNAME<>''''),IN_TITLE&'' ''&IN_LASTNAME&'','',''''))~ nos plus belles réalisations de 2015]]> 
     </OPTION> 
    </SUBJECT>' 

select rtrim((@XMLData).value('(/SUBJECT/OPTION)[1]','nvarchar(2000)')) 
+0

Nein, die Generierung des XML ist nicht unter meiner Kontrolle, also muss ich damit umgehen. Ersetzen Sie das '<>' behoben das Problem. Vielen Dank. – blackbishop

2

Die zusätzliche '<' in der '<>' Zeichenkombination verhindert die Konvertierung in XML. Also schlage ich vor, dass Sie die '<>' vor der Umwandlung der Zeichenfolge in XML loswerden. In meinem Beispiel ersetze ich es durch '! ='. Ich weiß, dass dies möglicherweise nicht genau zu Ihrer Situation passt, da Sie mit den Ergebnissen einer Abfrage mit dem Potenzial für mehrere Zeilen und nicht nur mit einer Variablen arbeiten müssen, aber hoffentlich gibt Ihnen das einige Ideen.

DECLARE @badStringPos int 

DECLARE @stringXMLData varchar(500)= '<SUBJECT> 
     <OPTION CONSTRAINT="MASTER.IN_TITLE = '' OR MASTER.IN_LASTNAME = ''"><![CDATA[Découvrez nos offres de location]]> 
     </OPTION> 
     <OPTION CONSTRAINT="IN_TITLE <> '' AND MASTER.IN_LASTNAME != ''"><![CDATA[~IN_TITLE~ ~IN_LASTNAME~, découvrez nos offres de location]]> 
     </OPTION> 
    </SUBJECT>' 

declare @XMLData xml 

set @badStringPos = patindex('%<>%', @stringXMLData) 

while @badStringPos <> 0 
begin 
    set @stringXMLData = left(@stringXMLData, patindex('%<>%', @stringXMLData) - 1) + '!=' + 
         right(@stringXMLData, len(@stringXMLData) - (patindex('%<>%', @stringXMLData) + 1)) 
    set @badStringPos = patindex('%<>%', @stringXMLData) 
end 

set @XMLData = convert(xml, @stringXMLData) 

select rtrim((@XMLData).value('(/SUBJECT/OPTION)[1]','nvarchar(2000)')) 

Eine Option wäre Iterate über Abfrageergebnisse mit einem Cursor.

+0

Ihre Idee ist grundsätzlich richtig, aber es war einfacher, die '<>' mit '< >' zu ersetzen. Der Vorteil ist, dass Sie den Inhalt nicht ändern würden ... – Shnugo

+0

Vielen Dank für Ihre Antwort. Das Ersetzen des '<>' hat die Aufgabe erledigt. Aber ich habe nur die 'Replace'-Funktion verwendet, wie von @Shnugo vorgeschlagen. – blackbishop

+0

Wow. @ Shnugos Antwort ist viel besser. Ich wollte die REPLACE-Funktion verwenden, aber der SSMS Intellisense/Tooltip hat mich getäuscht, weil ich dachte, ich könnte nur 1 Zeichen durch 1 anderes Zeichen ersetzen. Wie interpretiere ich "replace (Ausdruck nvarchar (1), Ausdruck nvarchar (1), Ausdruck nvarchar (1)) RETURNS nvarchar (1)"? – nscheaffer

1

Ich brauchte eine Weile, um herauszufinden, was hier falsch war - Sie haben einfache Anführungszeichen in Ihrer Zeichenfolge (2. Beispiel), sie müssen verdoppelt werden;

DECLARE @XMLData XML = '<SUBJECT> 
     <OPTION NAME="DEFAULT"><![CDATA[~(IF((IN_TITLE<>'' AND IN_LASTNAME<>''),IN_TITLE&'' ''&IN_LASTNAME&'','',''))~ nos plus belles réalisations de 2015]]> 
     </OPTION> 
    </SUBJECT>' 

select rtrim((@XMLData).value('(/SUBJECT/OPTION)[1]','nvarchar(2000)')) 
+0

Ich habe deine gewählt, weil ich das gleiche gefunden habe und eine Antwort platziert habe, aber du warst zuerst :-) – Shnugo