動的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

colsの生成(例と同じ)

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

何かを見逃したことがありますが、なぜ日付の一部しかユニコードに変換されないのですか?

どうすれば修正できますか?


これはどのバージョンのSQL Server用ですか?
ypercubeᵀᴹ

SQL Server 2012、しかしそれは重要ではありません、この場合に重要なのはxmlの仕様です
Bunkerbuster

これはXY問題のようです。XMLで属性名として日付を使用することは、これが意図したとおりに機能したとしても、お勧めできません。日付をどのように処理するかによって、日付を属性のまたは要素のテキストとして保存する傾向があります。必要に応じて、属性のペアを持つ複数の要素を作成します。
jpmc26 2016

回答:


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')が機能します。
バンカーバスター2016

6

最初の文字自体は、Unicodeではありません。つまり、技術的にはSQL Server内のXMLのすべての文字はUTF-16リトルエンディアンとしてエンコードされているので、その意味ではすべてUnicodeです。しかし、表示されているのは、文字のエスケープ表記だけです。この場合は「2」で、16進数/ 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" />
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.