Lors de la conversion de SQL dynamique (requête pivot) en sortie xml, pourquoi le premier chiffre de la date est-il converti en unicode?

11

J'utilise ce grand exemple /dba//a/25818/113298 de Bluefeet, pour créer un pivot et le transformer en données xml.

Déclarer le param

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

Ensuite, il y a un CTE avec beaucoup de code, le résultat final du CTE est mis dans une base de données temporaire (comme dans l'exemple)

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

Génération des cols (comme dans l'exemple)

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

L'ensemble de résultats est ce à quoi je devais m'attendre

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

entrez la description de l'image ici

Lorsque j'essaie de le transformer en XML, mes attributs ne sont que partiellement convertis

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 ;

jeu de résultats

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

Ai-je raté quelque chose, pourquoi seulement une partie de la date est-elle convertie en unicode?

Comment puis-je réparer cela?

Bunkerbuster
la source
À quelle version de SQL Server est-ce destiné?
ypercubeᵀᴹ
Sql Server 2012, mais ce n'est pas le but, ce sont les spécifications du xml qui sont importantes dans ce cas
Bunkerbuster
Cela semble être un problème XY. L'utilisation d'une date comme nom d'attribut dans XML semble peu judicieuse même si cela a fonctionné comme prévu. Je serais plus enclin à stocker la date en tant que valeur d'un attribut ou peut-être en tant que texte d'un élément, selon ce que je prévoyais de faire avec. Si nécessaire, je ferais plusieurs éléments avec des paires d'attributs.
jpmc26

Réponses:

14

Les noms d'attribut en XML ne peuvent pas commencer par un nombre, voir NameStartChar .

Vous devez trouver des noms alternatifs pour vos attributs et les coder dans une @colsvariable distincte spécifiant des alias de colonne pour votre requête de pivot dynamique.

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

Résultat;

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

Lorsque vous utilisez for xml autoSQL Server, cela est fait pour vous.

Mikael Eriksson
la source
C'était le lien manquant, également pour le chemin xml (''), root ('root') fonctionne maintenant.
Bunkerbuster
6

Le premier caractère n'est pas Unicode en soi. Je veux dire, techniquement, tous les caractères XML dans SQL Server sont encodés en UTF-16 Little Endian, donc dans ce sens, ils sont tous Unicode. Mais, ce que vous voyez est juste la notation échappée pour un caractère, dans ce cas "2", qui a une valeur hexadécimale / binaire de "32".

Le problème est simplement que les noms XML ne peuvent pas commencer par un nombre. Les tests suivants montrent qu'un nom d'attribut ou un nom d'élément commençant par un nombre obtient une erreur, mais commencer par un trait de soulignement ( _) ou une lettre est très 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>
*/

Vous devez donc préfixer les noms de colonne avec un caractère qui est valide comme caractère initial pour un attribut XML ou un nom d'élément.


De plus, êtes-vous sûr qu'il "fonctionne" avec FOR XML AUTO? D'après ce que je peux voir, il s'agit simplement de convertir automatiquement le caractère "invalide" en _x0032_:

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

Retour:

<tmp _x0032_016="2" />
Solomon Rutzky
la source