动态SQL(pivot查询)转换为xml输出时,为什么日期的第一位转换为unicode?

Bun*_*ter 11 xml sql-server pivot

我正在使用Bluefeet 的这个很好的例子https://dba.stackexchange.com/a/25818/113298,来创建一个数据透视表并将其转换为 xml 数据。

声明参数

DECLARE @cols AS NVARCHAR(MAX),  @query  AS NVARCHAR(MAX);
Run Code Online (Sandbox Code Playgroud)

接下来有一个代码很多的CTE,CTE的最终结果放在一个临时数据库中(与示例中相同)

SELECT 
B.[StayDate] -- this is a date dd-mm-yyyy
, B.[Guid]
INTO #tempDates
FROM BaseSelection B
Run Code Online (Sandbox Code Playgroud)

生成 cols(与示例相同)

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

结果集是我应该期望的

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

在此处输入图片说明

当我尝试将其转换为 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 ;
Run Code Online (Sandbox Code Playgroud)

结果集

<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" 
/>
Run Code Online (Sandbox Code Playgroud)

我错过了什么,为什么只有一部分日期转换为 unicode?

我怎样才能解决这个问题?

Mik*_*son 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,'');
Run Code Online (Sandbox Code Playgroud)

结果;

[2016-12-20] as [z2016-12-20],[2016-12-21] as [z2016-12-21]
Run Code Online (Sandbox Code Playgroud)
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,'');
Run Code Online (Sandbox Code Playgroud)

当您使用for xml autoSQL Server 时,它会为您执行此操作。


Sol*_*zky 6

第一个字符本身不是 Unicode。我的意思是,从技术上讲,SQL Server 中 XML 中的所有字符都编码为 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>
*/
Run Code Online (Sandbox Code Playgroud)

因此,您需要在列名前添加一个字符作为 XML 属性或元素名称的初始字符。


另外,您确定它正在“工作”FOR XML AUTO吗?从我所见,它只是将“无效”字符自动转换为_x0032_

SELECT tmp.* FROM (SELECT 2) tmp([2016]) FOR XML AUTO;
Run Code Online (Sandbox Code Playgroud)

返回:

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>
*/
Run Code Online (Sandbox Code Playgroud)