McN*_*ets 6 xml sql-server cast sql-server-2014
我需要深入研究具有类似于此架构的日志表:
CREATE TABLE t (
id int PRIMARY KEY,
data varchar(max)
);
Run Code Online (Sandbox Code Playgroud)
列data
以这种格式存储从 Web 服务接收的 XML 文本:
这是缩小版
<?xml version="1.0" encoding="UTF-8"?>
<PARAM>
<TAB DIM="30" ID="ZC3D2_1" SIZE="5">
<LIN NUM = "1">
<FLD NAME = "ZDOC" TYPE = "Char">Ferran López</FLD>
</LIN>
</TAB>
</PARAM>
Run Code Online (Sandbox Code Playgroud)
当我尝试将此文本转换为 XML 时,出现下一个错误:
XML 解析:第 xx 行,字符 48,非法 xml 字符
可以通过删除<xml>
标记或至少删除encoding
属性来解决。
注意:如果没有像 那样的特殊字符
ó
,即使我不删除<xml>
标签,它也能正常工作。
题
有没有办法在不替换或删除<xml>
标签的情况下将其转换为 XML ?
CAST(REPLACE(data, 'encoding="UTF-8"', '') as XML)
Run Code Online (Sandbox Code Playgroud)
db<>在这里摆弄
更新
服务器整理是:Latin1_General_BIN
但即使我尝试将排序规则更改为我常用的服务器排序规则,它也不起作用。
SELECT
id,
CAST((data COLLATE Latin1_General_CI_AS) as XML)
FROM
t;
Run Code Online (Sandbox Code Playgroud)
存储在 varchar(max) 列中的 XML 应如下所示。
<?xml version="1.0" encoding="UTF-8"?>
<PARAM>
<TAB DIM="30" ID="ZC3D2_1" SIZE="5">
<LIN NUM = "1">
<FLD NAME = "ZDOC" TYPE = "Char">Ferran López</FLD>
</LIN>
</TAB>
</PARAM>
Run Code Online (Sandbox Code Playgroud)
在ó
应与双字节值来表示ó
。
如果您的列中没有存储 UTF-8 编码的字符串,那么正确的方法是在将值转换为 XML 数据类型之前从 XML 中删除编码。
我认为你有更深层次的问题。UTF-8 允许比 SQL 服务器中的常规非 Unicode 排序规则更多的字符。因此,为了安全起见,您应该使用具有 UTF-8 排序规则的 SQL Server 2019(我理解这是否由于多种原因不可行/不可取)操作系统使用(尝试)nvarchar 而不是 varchar。
如果您害怕从 varchar 到 nvarchar 的存储增加,您可以使用行压缩。但这需要 SQL Server 2016 之前的企业版。
这里发生的事情是:
XML
类型在内部将数据存储为 UTF-16 Little Endian(至少在大多数情况下)。源编码是什么并不重要,最终结果将是 UTF-16 LE(并且没有<xml>
标签,因此没有encoding="..."
)。XML
:
NVARCHAR
数据假定为 UTF-16 LE。如果有一个<xml>
标签并且它包含该encoding
属性,则唯一有效的值是"UTF-16"
。VARCHAR
当没有<xml>
标签时,或者如果存在标签但没有encoding
属性,则假定数据位于与数据整理相关联的 8 位代码页中。否则,数据将被解释为在encoding
属性中指定的代码页中编码(即使它是在与数据整理相关联的代码页中编码的)。Latin1_General_BIN
,因此是安全的- 足以假设该列使用相同的排序规则)。ó
代码页 Windows-1252 中字符的代码点是:0xF3。<xml>
但是,该标记声明 XML 数据被编码为 UTF-8。p
,其中有一个值0x70。因此,您会收到“非法 xml 字符”错误(因为它encoding="UTF-8"
告诉转换函数字节是有效的 UTF-8 字节;转换没有看到该ó
字符)。您的选择是:
理想地,该柱将被转换为XML
和所述encoding
的的属性<xml>
标签,或整个<xml>
标签本身,将在所述的方式去除。AND,该XML
数据类型可以节省空间,如果有重复元件和/或属性名称为它创建了一个内部名称的字典(查找列表)并使用 ID 值记录结构。
将[data]
列设置为使用 UTF-8 排序规则(SQL Server 2019 中的新增功能,因此不适合您)
将[data]
列设置为NVARCHAR
,并删除标签的encoding
属性<xml>
,或整个<xml>
标签。
将传入的字符串转换为 UTF-8 字节。所以ó
字符是 UTF-8 中的两个字节:0xC3B3,它出现ó
在 Windows-1252 中。
DECLARE @Good VARCHAR(MAX) = '<?xml version="1.0" encoding="UTF-8"?><a>hell'
+ CONVERT(VARCHAR(MAX), 0xC3B3)
+ '</a>';
SELECT @Good, CONVERT(XML, @Good)
-- <?xml version="1.0" encoding="UTF-8"?><a>helló</a>
--
-- <a>helló</a>
Run Code Online (Sandbox Code Playgroud)
笔记:
encoding
属性<xml>
或整个<xml>
标签不是一种选择。当然,它会在这种特殊情况下工作,但它不会在所有情况下都有效,因为 SQL Server 2014 中的列VARCHAR
和UTF-8 排序规则不可用。因此,Windows 代码页 1252 中不可用的任何 Unicode 字符都将转换为?
或??
(取决于 BMP 字符或补充字符):
DECLARE @Test VARCHAR(MAX) = '<test>ó - ? - </test>';
SELECT @Test, CONVERT(XML, @Test);
-- <test>ó - ? - ??</test>
--
-- <test>ó - ? - ??</test>
Run Code Online (Sandbox Code Playgroud)
DECLARE @Data NVARCHAR(MAX) = N'ó';
SELECT CONVERT(VARCHAR(MAX), @Data COLLATE Latin1_General_BIN) AS [Latin1_General],
CONVERT(VARCHAR(MAX), @Data COLLATE Latin1_General_BIN) COLLATE
Cyrillic_General_CI_AS AS [Cyrillic];
/*
Latin1_General Cyrillic
ó o
*/
Run Code Online (Sandbox Code Playgroud)
“Cyrillic”使用与“Latin1_General”不同的代码页,并且该ó
字符在 Cyrillic 代码页上不可用。但是,有一个“最佳拟合”映射,这就是为什么我们最终得到一个o
而不是一个?
._100_
级别排序规则。此外,任何在 SQL Server 2012 或更高版本上工作的人都应该使用_100_
以_SC
(对于补充字符)结尾的级别排序规则。最后,需要SQL Server 2005或更新,使用一个在结束对二进制归类时_BIN2
(参见我的文章在这里,为什么)。 归档时间: |
|
查看次数: |
2177 次 |
最近记录: |