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


11

我使用的是来自Bluefeet的出色示例/dba//a/25818/113298,以创建数据透视并将其转换为xml数据。

声明参数

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

接下来是具有大量代码的CTE,将CTE的结果放入临时DB(与示例中相同)

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

生成col(与示例相同)

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 2016年

这似乎是一个XY问题。即使按预期工作,在XML中使用日期作为属性名称似乎也不建议。我更倾向于将日期存储为属性的或元素的文本,具体取决于我打算如何处理。如有必要,我将使用多个具有成对属性的元素。
jpmc26 2013年

Answers:


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')现在正在工作。
Bunkerbuster '16

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

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


另外,您确定它可以“使用” FOR XML AUTO吗?据我所知,它只是将“无效”字符自动转换为_x0032_

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

返回值:

<tmp _x0032_016="2" />
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.