Ao converter SQL dinâmico (consulta dinâmica) em saída xml, por que o primeiro dígito da data é convertido em unicode?


11

Estou usando este ótimo exemplo /dba//a/25818/113298 da Bluefeet, para criar um pivô e transformá-lo em dados xml.

Declarando o parâmetro

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

Em seguida, há um CTE com muito código, o resultado final do CTE é colocado em um banco de dados temporário (o mesmo que no exemplo)

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

Gerando as colunas (igual ao exemplo)

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

O conjunto de resultados é o que eu deveria 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 ;

insira a descrição da imagem aqui

Quando tento transformá-lo em XML, meus atributos são convertidos apenas 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 de resultados

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

Perdi alguma coisa, por que apenas uma parte da data é convertida em unicode?

Como posso consertar isso?


Qual é a versão do SQL Server?
ypercubeᵀᴹ

Sql Server 2012, mas isso não é o ponto, é as especificações do xml que é importante neste caso
Bunkerbuster

Parece um problema XY. Usar uma data como um nome de atributo em XML parece desaconselhável, mesmo que funcione conforme o esperado. Eu estaria mais inclinado a armazenar a data como o valor de um atributo ou talvez como o texto de um elemento, dependendo do que planejei fazer com ele. Se necessário, eu faria vários elementos com pares de atributos.
precisa saber é o seguinte

Respostas:


14

Os nomes de atributo em XML não podem começar com um número, consulte NameStartChar .

Você precisa criar nomes alternativos para seus atributos e codificá-los em uma @colsvariável separada especificando aliases de coluna para sua 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" />

Quando você está usando o for xml autoSQL Server, faz isso por você.


Esse era o link que faltava, também para o caminho xml (''), o root ('root') está funcionando agora.
precisa saber é o seguinte

6

O primeiro caractere não é Unicode, por si só. Quero dizer, tecnicamente todos os caracteres em XML no SQL Server são codificados como UTF-16 Little Endian, portanto, nesse sentido, eles são todos Unicode. Mas o que você está vendo é apenas a notação de escape de um personagem, neste caso "2", que tem um valor hexadecimal / binário de "32".

O problema é simplesmente que os nomes XML não podem começar com um número. Os testes a seguir mostram que um nome de atributo ou elemento começando com um número recebe um erro, mas começar com um sublinhado ( _) ou uma letra é bom.

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

Portanto, você precisa prefixar os nomes das colunas com um caractere válido como caractere inicial para um atributo XML ou nome do elemento.


Além disso, você tem certeza de que está "trabalhando" FOR XML AUTO? Pelo que posso ver, é simplesmente a conversão automática do caractere "inválido" para _x0032_:

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

Devoluções:

<tmp _x0032_016="2" />
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.