从生产到测试环境,一段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。
答案 0 :(得分:1)
在SQL Server中,您应该将XML存储在键入XML
的列中。这种原生类型有很多优点。它快得多并且具有隐式有效性检查。
根据您提出的问题,您将XML存储在NTEXT
中。几个世纪以来,此类型已弃用,未来版本中将不再受支持! 你应该尽快改变这一点!
SQL-Server知道两种字符串:
CHAR
或VARCHAR
),扩展ASCII NCHAR
或NVARCHAR
), UTF-16 (UCS-2)如果XML具有带编码的前导声明(在大多数情况下为utf-8
或utf-16
),则可能会遇到麻烦。
如果XML存储为 2-byte-string (至少NTEXT
告诉我这个),声明必须是utf-16
。使用 1字节字符串时,它应为utf-8
。
最好(也是最简单)是完全省略声明。你不需要它。以适当的类型存储XML将自动终止此声明。
您应该做什么:创建一个类型为XML
的新列,并将所有XML混合到此列。摆脱您可能拥有的任何TEXT
,NTEXT
和IMAGE
列!
下一步是:快乐并享受原生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);
使用VARCHAR
和NVARCHAR
以及utf-8
和utf-16
...