Warum wird beim Konvertieren von dynamischem SQL (Pivot-Abfrage) in XML-Ausgabe die erste Ziffer des Datums in Unicode konvertiert?

11

Ich verwende dieses großartige Beispiel /dba//a/25818/113298 von Bluefeet, um einen Pivot zu erstellen und ihn in XML-Daten umzuwandeln.

Den Parameter deklarieren

DECLARE @cols AS NVARCHAR(MAX),  @query  AS NVARCHAR(MAX);

Als nächstes gibt es einen CTE mit viel Code, das Endergebnis des CTE wird in eine temporäre Datenbank gestellt (wie im Beispiel).

SELECT 
B.[StayDate] -- this is a date dd-mm-yyyy
, B.[Guid]
INTO #tempDates
FROM BaseSelection B

Generieren der Spalten (wie im Beispiel)

SELECT @cols = STUFF((SELECT distinct ',' +QUOTENAME(convert(char(10), [StayDate] , 120)) 
FROM #tempDates
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)') 
,1,1,'');

Die Ergebnismenge ist das, was ich erwarten sollte

set @query = 
   'SELECT [Guid],' + @cols +'
    FROM
    (
        SELECT 
            [StayDate] 
           ,[Guid]
        FROM #tempDates
    ) A
    pivot
    (
        count([StayDate])
        for [StayDate] in (' + @cols +')                    
    ) p
    '
EXEC sp_executesql  @query ;

Geben Sie hier die Bildbeschreibung ein

Wenn ich versuche, es in XML umzuwandeln, werden meine Attribute nur teilweise konvertiert

set @query = 
   'SELECT [Guid],' + @cols +'
    FROM
    (
        SELECT 
            [StayDate] 
           ,[Guid]
        FROM #tempDates
    ) A
    pivot
    (
        count([StayDate])
        for [StayDate] in (' + @cols +')                    
    ) p
    for xml auto
    -- when using for XML path i will get a error
    -- FOR XML PATH(''''), ROOT(''root'') 
    -- Msg 6850, Level 16, State 1, Line 3
    -- Column name '2016-12-17' contains an invalid XML identifier 
    -- as required by FOR XML; '2'(0x0032) is the first character at fault.
    '
EXEC sp_executesql  @query ;

Ergebnismenge

<p Guid="3C3359E3-CFE5-E511-80CA-005056A90901"
  _x0032_016-12-17="2" --> should be 2016-12-17="2" 
  _x0032_016-12-18="2" --> should be 2016-12-18="2" 
  _x0032_016-12-19="2" --> should be 2016-12-19="2" 
/>

Habe ich etwas verpasst, warum wird nur ein Teil des Datums in Unicode konvertiert?

Wie kann ich das beheben?

Bunkerbuster
quelle
Für welche Version von SQL Server ist das?
Ypercubeᵀᴹ
SQL Server 2012, aber das ist nicht der Punkt, es sind die Spezifikationen der XML, die in diesem Fall wichtig sind
Bunkerbuster
Dies scheint ein XY-Problem zu sein. Die Verwendung eines Datums als Attributname in XML scheint nicht ratsam zu sein, selbst wenn dies wie beabsichtigt funktioniert hat. Ich würde eher das Datum als Wert eines Attributs oder vielleicht als Text eines Elements speichern , je nachdem, was ich damit vorhatte. Bei Bedarf würde ich mehrere Elemente mit Attributpaaren erstellen.
jpmc26

Antworten:

14

Attributnamen in XML dürfen nicht mit einer Zahl beginnen, siehe NameStartChar .

Sie müssen alternative Namen für Ihre Attribute finden und diese in einer separaten @colsVariablen codieren, die Spaltenaliasnamen für Ihre dynamische Pivot-Abfrage angibt.

SELECT @cols2 = STUFF((SELECT distinct ',' +
                       quotename(convert(char(10), [StayDate] , 120)) + 
                       ' as '+ QUOTENAME('z'+convert(char(10), [StayDate] , 120)) 
FROM #tempDates
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)') 
,1,1,'');

Ergebnis;

[2016-12-20] as [z2016-12-20],[2016-12-21] as [z2016-12-21]
<p Guid="6365FC57-F476-4703-B9D4-1EB81288FF30" z2016-12-20="0" z2016-12-21="1" />
<p Guid="B38FA9DB-B4E1-4725-8F3B-3AF6E009C10A" z2016-12-20="1" z2016-12-21="0" />

Wenn Sie verwenden for xml auto SQL Server verwenden, erledigt dies für Sie.

Mikael Eriksson
quelle
Das war das fehlende Glied, auch für den XML-Pfad ('') funktioniert root ('root') jetzt.
Bunkerbuster
6

Das erste Zeichen ist per se kein Unicode. Ich meine, technisch gesehen sind alle Zeichen in XML in SQL Server als UTF-16 Little Endian codiert. In diesem Sinne sind sie alle Unicode. Was Sie jedoch sehen, ist nur die Escape-Notation für ein Zeichen, in diesem Fall "2", das einen Hex / Binär-Wert von "32" hat.

Das Problem ist einfach, dass XML-Namen nicht mit einer Nummer beginnen können. Die folgenden Tests zeigen, dass ein Attribut- oder Elementname, der mit einer Zahl beginnt, einen Fehler erhält, aber mit einem Unterstrich ( _) oder einem Buchstaben zu beginnen, ist in Ordnung.

SELECT CONVERT(XML, N'<test><row 2016-12-17="2" /></test>');
/*
Msg 9455, Level 16, State 1, Line 10
XML parsing: line 1, character 12, illegal qualified name character
*/


SELECT CONVERT(XML, N'<test><2016>a</2016></test>');
/*
Msg 9455, Level 16, State 1, Line 10
XML parsing: line 1, character 8, illegal qualified name character
*/


SELECT CONVERT(XML, N'<test><row _2016-12-17="2" /></test>');
/*
<test>
  <row _2016-12-17="2" />
</test>
*/


SELECT CONVERT(XML, N'<test><row x2016-12-17="2" /></test>');
/*
<test>
  <row x2016-12-17="2" />
</test>
*/

Daher müssen Sie den Spaltennamen ein Zeichen voranstellen, das als Anfangszeichen für ein XML-Attribut oder einen Elementnamen gültig ist.


Sind Sie auch sicher, dass es "funktioniert" FOR XML AUTO? Soweit ich sehen kann, wird das "ungültige" Zeichen einfach automatisch konvertiert in _x0032_:

SELECT tmp.* FROM (SELECT 2) tmp([2016]) FOR XML AUTO;

Kehrt zurück:

<tmp _x0032_016="2" />
Solomon Rutzky
quelle