Quando si converte SQL dinamico (query pivot) in output xml, perché la prima cifra della data viene convertita in unicode?


11

Sto usando questo fantastico esempio /dba//a/25818/113298 di Bluefeet, per creare un perno e trasformarlo in dati XML.

Dichiarando il param

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

Successivamente c'è un CTE con molto codice, l'endresult del CTE viene inserito in un DB temporaneo (come nell'esempio)

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

Generare i col (come nell'esempio)

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

Il set di risultati è quello che dovrei aspettarmi

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

inserisci qui la descrizione dell'immagine

Quando provo a trasformarlo in XML, i miei attributi vengono convertiti solo parzialmente

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 ;

di risultati

<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" 
/>

Ho perso qualcosa, perché solo una parte della data viene convertita in Unicode?

Come posso risolvere questo problema?


Per quale versione di SQL Server si tratta?
— ypercubeᵀᴹ

SQL Server 2012, ma non è questo il punto, sono le specifiche
— dell'xml

Sembra un problema XY. L'uso di una data come nome di attributo in XML sembra sconsigliato anche se funzionava come previsto. Sarei più propenso a memorizzare la data come valore di un attributo o forse come testo di un elemento, a seconda di ciò che avevo pianificato di fare con esso. Se necessario, creerei più elementi con coppie di attributi.
— jpmc26,

Risposte:


14

I nomi degli attributi in XML non possono iniziare con un numero, vedere NameStartChar .

Devi trovare nomi alternativi per i tuoi attributi e codificarli in una @colsvariabile separata specificando gli alias di colonna per la tua query pivot dinamica.

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,'');

Risultato;

[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" />

Quando si utilizza for xml autoSQL Server lo fa per te.


Quello era il collegamento mancante, anche per il percorso xml (''), root ('root') ora funziona.
— Bunkerbuster,

6

Il primo personaggio non è Unicode, di per sé. Voglio dire, tecnicamente tutti i caratteri in XML all'interno di SQL Server sono codificati come UTF-16 Little Endian, quindi in questo senso sono tutti Unicode. Ma quello che stai vedendo è solo la notazione di escape per un personaggio, in questo caso "2", che ha un valore esadecimale / binario di "32".

Il problema è semplicemente che i nomi XML non possono iniziare con un numero. I seguenti test mostrano che un nome di attributo o un elemento che inizia con un numero ottiene un errore, ma che inizia con un trattino basso ( _) o una lettera va bene.

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>
*/

Pertanto, è necessario aggiungere un prefisso ai nomi delle colonne con un carattere valido come carattere iniziale per un attributo XML o un nome di elemento.


Inoltre, sei sicuro che stia "lavorando" con FOR XML AUTO? Da quello che posso vedere, sta semplicemente convertendo automaticamente il carattere "non valido" in _x0032_:

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

Ritorna:

<tmp _x0032_016="2" />
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.