При перетворенні динамічного SQL (півостротного запиту) на вихід xml, чому перша цифра дати перетворюється в unicode?


11

Я використовую цей чудовий приклад /dba//a/25818/113298 від Bluefeet, щоб створити поворот і перетворити його на XML-дані.

Оголошення парам

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

Далі є CTE з великою кількістю коду, кінцевий результат CTE вводиться в temp БД (такий же, як у прикладі)

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

Генерація значень (те саме, що в прикладі)

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

Набір результатів - це те, чого я повинен очікувати

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

введіть тут опис зображення

Коли я намагаюся перетворити його в XML, мої атрибути перетворюються лише частково

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 ;

набір результатів

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

Я щось пропустив, чому лише частина дати перетворена на unicode?

Як я можу це виправити?


Для якої версії SQL Server це?
ypercubeᵀᴹ

Sql Server 2012, але в цьому справа не в тому, що в цьому випадку важливі характеристики xml
Bunkerbuster

Це здається проблемою XY. Використання дати як імені атрибута в XML видається нерекомендованим, навіть якщо це працювало за призначенням. Я б більше схильний зберігати дату як значення атрибута або, можливо, як текст елемента, залежно від того, що я планував зробити з ним. Якщо потрібно, я зробив би кілька елементів з парами атрибутів.
jpmc26

Відповіді:


14

Імена атрибутів у XML заборонено починати з числа, див. NameStartChar .

Ви повинні придумати альтернативні імена для своїх атрибутів і закодувати їх в окремій @colsзмінній із зазначенням псевдонімів стовпців для вашого динамічного запиту зведення.

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

Результат;

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

Коли ви використовуєте for xml autoSQL Server, це робить для вас.


Це було відсутнім посиланням, також для xml path (''), root ('root') зараз працює.
Бункербастер

6

Перший символ не є Unicode, сам по собі. Я маю на увазі, що технічно всі символи XML в SQL Server кодуються як UTF-16 Little Endian, тому в цьому сенсі вони всі Unicode. Але те, що ви бачите, - це лише втечене позначення для персонажа, у цьому випадку "2", яке має шістнадцяткове / двійкове значення "32".

Проблема полягає лише в тому, що імена XML не можуть починатися з числа. Наступні тести показують, що ім’я атрибута або ім’я елемента, що починається з числа, отримує помилку, але починаючи з підкреслення ( _) або літери просто добре.

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

Отже, вам потрібно встановити префікс імен стовпців із символом, який дійсний як початковий символ для атрибута XML або імені елемента.


Також ви впевнені, що це "працює" FOR XML AUTO? З того, що я бачу, це просто автоматичне перетворення "недійсного" символу в _x0032_:

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

Повернення:

<tmp _x0032_016="2" />
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.