消息9402,级别16,状态1,行9 XML解析:第1行,字符38,无法切换编码

时间:2017-07-03 18:35:48

标签: sql-server xml tsql casting character-encoding

从生产到测试环境,一段tsql代码的行为不同。当在prod上执行下面的代码时,它会带回数据

SELECT [col1xml]

FROM [DBName].[dbo].[Table1] (NOLOCK)

WHERE (cast([col1xml] as xml).value('(/Payment/****/trn1)[1]','nvarchar(20)') ='123456'))

但是,在Test中运行时,相同的代码会返回以下错误。

  

Msg 9402,Level 16,State 1,Line 9   XML解析:第1行,第38个字符,无法切换编码

我已经看到了这个UTF转换站点提供的修复,这在prod和test中都有效。见下文。但是,我需要向开发人员提供一个答案,说明为什么会出现这种行为,以及为什么他们应该更改代码的理由(如果是这种情况)

WHERE CAST(

REPLACE(CAST(col1xml AS VARCHAR(MAX)), 'encoding="utf-16"', 'encoding="utf-8"')

AS XML).value('(/Payment/****/trn1)[1]','NVARCHAR(max)') ='123456')

我比较了两个数据库,并寻找任何明显的东西,例如ansi nulls和ansi padding。一切都和SQL Server的版本一样。这是SQL SERVER 2012 11.0.5388版本。环境之间的数据不同,但表模式是相同的,col1xml的数据类型是ntext。

1 个答案:

答案 0 :(得分:1)

在SQL Server中,您应该将XML存储在键入XML的列中。这种原生类型有很多优点。它快得多并且具有隐式有效性检查。

根据您提出的问题,您将XML存储在NTEXT中。几个世纪以来,此类型已弃用,未来版本中将不再受支持你应该尽快改变这一点!

SQL-Server知道两种字符串:

  • 1字节字符串(CHARVARCHAR),扩展ASCII
    重要提示:这不是UTF-8!原生UTF-8支持将成为即将推出的版本的一部分。
  • 2字节字符串(NCHARNVARCHAR), UTF-16 (UCS-2)

如果XML具有带编码的前导声明(在大多数情况下为utf-8utf-16),则可能会遇到麻烦。

如果XML存储为 2-byte-string (至少NTEXT告诉我这个),声明必须是utf-16 。使用 1字节字符串时,它应为utf-8

最好(也是最简单)是完全省略声明。你不需要它。以适当的类型存储XML将自动终止此声明。

您应该做什么:创建一个类型为XML的新列,并将所有XML混合到此列。摆脱您可能拥有的任何TEXTNTEXTIMAGE列!

下一步是:快乐并享受原生XML类型的快速轻松:-D

更新环境差异

您写道:环境之间的数据不同

错误发生在这里:

cast([col1xml] as xml)

如果您的列将XML存储在本机类型中,则根本不需要强制转换(非常昂贵!! )。但在你的情况下,这个演员表取决于实际的XML。由于它存储在NTEXT中,因此它是 2字节字符串。如果您的XML以声明声明不受支持的编码(大多数情况下为utf-8)开头,则会失败。

试试这个:

这有效

DECLARE @xml2Byte_UTF16 NVARCHAR(100)='<?xml version="1.0" encoding="utf-16"?><root>test1</root>';
SELECT CAST(@xml2Byte_UTF16 AS XML);

DECLARE @xml1Byte_UTF8 VARCHAR(100)='<?xml version="1.0" encoding="utf-8"?><root>test2</root>';
SELECT CAST(@xml1Byte_UTF8 AS XML);

这失败

DECLARE @xml2Byte_UTF8 NVARCHAR(100)='<?xml version="1.0" encoding="utf-8"?><root>test3</root>';
SELECT CAST(@xml2Byte_UTF8 AS XML);

DECLARE @xml1Byte_UTF16 VARCHAR(100)='<?xml version="1.0" encoding="utf-16"?><root>test4</root>';
SELECT CAST(@xml1Byte_UTF16 AS XML);

使用VARCHARNVARCHAR以及utf-8utf-16 ...