Al convertir SQL dinámico (consulta dinámica) en salida xml, ¿por qué el primer dígito de la fecha se convierte en unicode?


11

Estoy usando este gran ejemplo /dba//a/25818/113298 de Bluefeet, para crear un pivote y transformarlo en datos xml.

Declarando el param

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

Luego hay un CTE con mucho código, el resultado final del CTE se coloca en una base de datos temporal (igual que en el ejemplo)

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

Generando los cols (igual que el ejemplo)

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

El conjunto de resultados es lo que debería esperar

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

ingrese la descripción de la imagen aquí

Cuando intento transformarlo a XML, mis atributos solo se convierten parcialmente

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 ;

conjunto resultante

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

¿Me he perdido algo? ¿Por qué solo una parte de la fecha se convierte a Unicode?

¿Cómo puedo arreglar esto?


¿Para qué versión de SQL Server es esto?
ypercubeᵀᴹ

Sql Server 2012, pero ese no es el punto, son las especificaciones del xml lo que es importante en este caso
Bunkerbuster

Esto parece un problema XY. El uso de una fecha como nombre de atributo en XML parece desaconsejado incluso si esto funcionó según lo previsto. Estaría más inclinado a almacenar la fecha como el valor de un atributo o tal vez como el texto de un elemento, dependiendo de lo que planee hacer con él. Si es necesario, haría múltiples elementos con pares de atributos.
jpmc26

Respuestas:


14

Los nombres de atributos en XML no pueden comenzar con un número, consulte NameStartChar .

Debe encontrar nombres alternativos para sus atributos y codificarlos en una @colsvariable separada que especifique los alias de columna para su consulta dinámica dinámica.

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

Resultado;

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

Cuando usa for xml autoSQL Server, lo hace por usted.


Ese era el enlace que faltaba, también para la ruta xml (''), la raíz ('root') ahora está funcionando.
Bunkerbuster

6

El primer personaje no es Unicode, per se. Quiero decir, técnicamente todos los caracteres en XML dentro de SQL Server están codificados como UTF-16 Little Endian, por lo que en ese sentido son todos Unicode. Pero, lo que está viendo es solo la notación escapada de un personaje, en este caso "2", que tiene un valor hexadecimal / binario de "32".

El problema es simplemente que los nombres XML no pueden comenzar con un número. Las siguientes pruebas muestran que un nombre de atributo o nombre de elemento que comienza con un número obtiene un error, pero comenzar con un guión bajo ( _) o una letra está bien.

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

Por lo tanto, debe anteponer los nombres de columna con un carácter que sea válido como carácter inicial para un atributo XML o nombre de elemento.


Además, ¿estás seguro de que está "trabajando" FOR XML AUTO? Por lo que puedo ver, es simplemente convertir automáticamente el carácter "no válido" a _x0032_:

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

Devoluciones:

<tmp _x0032_016="2" />
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.