【问题标题】:Msg 9402, Level 16, State 1, Line 9 XML parsing: line 1, character 38, unable to switch the encodingMsg 9402, Level 16, State 1, Line 9 XML解析:第1行,字符38,无法切换编码
【发布时间】:2017-07-03 18:35:48
【问题描述】:

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

SELECT [col1xml]

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

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

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

消息 9402,第 16 级,状态 1,第 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 null 和 ansi 填充。一切都和 SQL Server 的版本一样。这是 SQL SERVER 2012 11.0.5388 版本。环境之间的数据不同,但表架构相同,col1xml的数据类型为ntext。

【问题讨论】:

  • 因为 XML 的编码(隐式或显式)完全由底层字符串类型确定,所以您的 XML 文档应该包含编码指令——这些指令除了采取占用空间并可能使解析器绊倒。如果无法避免获取它们,最好在存储它们时完全剥离它们(也就是说,不要用encoding="utf-8" 替换它们,用nothing 替换它们)。可以说服大多数 XML 库不输出 XML 声明,或者至少不输出带有编码的声明。

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


【解决方案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 字节字符串(至少 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);

玩转VARCHARNVARCHARutf-8utf-16...

【讨论】:

  • CHARVARCHAR 包含“非 Unicode 数据”。使用的排序规则不需要与 ASCII 兼容,称它们为“扩展 ASCII”是一种误导。以防万一:SQL_EBCDIC037_CP1_CS_AS。 (我确实认为所有支持的排序规则至少都是单字节的。)另外,您是否有声称在未来版本中CHAR 存储将支持 UTF-8 的来源?我所知道的是从 SQL Server 2016 开始支持批量导入/导出操作中的代码页 65001,这不是一回事。
  • 谢谢。这都是有用的信息。但我仍然无法发现环境之间的行为差​​异。我检查了数据库、表和列级别的排序规则。我肯定会反馈 XML 数据类型和 ntext 被弃用。
  • @JeroenMostert Uhm... 这个Connect article 不支持我的说法。由于数据库的性质,很清楚为什么固定字节宽度会胜出...我浏览了 SQL Server 2016 中的新功能? 列表并看到标题 support for UTF -8,但你是对的:this is not touching normal string handling, just BCP, BULK etc.
  • @ADTJOB 查看我的更新以解释环境差异
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-12
  • 1970-01-01
  • 2021-03-18
相关资源
最近更新 更多