UTF-8支持,SQL Server 2012和UTF8String UDT

3

研究SQL Server的VARCHAR与NVARCHAR在我的特定应用程序中的优缺点时,我意识到如果SQL Server原生支持UTF8将是理想的。几篇SO文章表明它不支持,例如:

VARCHAR完全像1990年代吗?

varchar和nvarchar SQL Server数据类型之间的主要性能差异是什么?

然而,我后来发现了SQL Server 2012 MSDN文档中的这篇文章,介绍了如何创建一个UTF8String用户定义数据类型:

http://msdn.microsoft.com/en-us/library/ff877964(v=sql.110).aspx

似乎UDT可以在存储8位每个字符的空间(内存、磁盘)的同时,足够灵活地存储任何可以用UTF-8表示的字符串。这是正确的吗?这种策略有什么缺点吗(例如执行每行托管代码的性能成本等)?
1个回答

2
通过SQLCLR创建自定义用户定义类型,并不会以任何方式取代任何本地类型。它非常方便用于创建处理专业数据的东西。但是字符串,即使是不同编码的字符串,也远非专业化。如果您选择这种方式来处理字符串数据,将破坏系统的任何可用性,更不用说性能了,因为您将无法使用任何内置字符串函数。
如果您能够在磁盘空间上节省任何内容,那么这些收益将被整体性能损失所抵消。存储UDT是通过将其序列化为VARBINARY来完成的。因此,为了进行任何字符串比较或排序(除了“二进制”/“序数”比较之外),您必须逐个将所有其他值转换回UTF-8,然后进行可以考虑语言差异的字符串比较。并且该转换需要在UDT内完成。这意味着,就像XML数据类型一样,您将创建UDT以保存特定值,然后公开该UDT的方法以接受字符串参数以进行比较(即Utf8String.Compare(alias.field1)或者,如果为该类型定义运算符,则Utf8string1 = Utf8string2并且使=运算符获取UTF-8编码的字符串,然后进行CompareInfo.Compare())。
除上述考虑因素外,您还需要考虑通过SQLCLR API来回传递值的成本,特别是使用NVARCHAR(MAX)VARBINARY(MAX)而不是分别使用NVARCHAR(1-4000)VARBINARY(1-4000)(请不要将此区别与使用SqlChars / SqlBytesSqlString / SqlBinary有任何关系)。
最后(至少在使用UDT方面),请不要忽略正在查询的UDT是示例代码这一事实。唯一注明的测试纯粹是功能性的,没有关于可扩展性或“使用它工作了一年后得出的经验教训”。功能测试代码在以下CodePlex页面中显示,并且应在决定继续使用之前查看它,因为它可以让您了解如何编写与其交互的查询(对于一个或两个字段来说是可以的,但对于大多数/所有字符串字段来说不行): http://msftengprodsamples.codeplex.com/SourceControl/latest#Kilimanjaro_Trunk/Programmability/CLR/UTF8String/Scripts/Test.sql

考虑到已添加的计算列和索引数量,是否真的节省了空间呢?;-)


如果考虑到空间(磁盘、内存等)的问题,您有三个选择:

  1. If you are using SQL Server 2008 or newer, and are on Enterprise Edition, then you can enable Data Compression. Data Compression can (but won't "always") compress Unicode data in NCHAR and NVARCHAR fields. The determining factors are:

    1. NCHAR(1 - 4000) and NVARCHAR(1 - 4000) use the Standard Compression Scheme for Unicode, but only starting in SQL Server 2008 R2, AND only for IN ROW data, not OVERFLOW! This appears to be better than the regular ROW / PAGE compression algorithm.
    2. NVARCHAR(MAX) and XML (and I guess also VARBINARY(MAX), TEXT, and NTEXT) data that is IN ROW (not off row in LOB or OVERFLOW pages) can be at least PAGE compressed, and maybe also ROW compressed (not sure about this last one).
    3. Any OFF ROW data, LOB or OVERLOW = No Compression For You!
  2. If using a version older than 2008 or not on Enterprise Edition, you can have two fields: one VARCHAR and one NVARCHAR. For example, let's say you are storing URLs which are mostly all base ASCII characters (values 0 - 127) and hence fit into VARCHAR, but sometimes have Unicode characters. Your schema can include the following 3 fields:

      ...
      URLa VARCHAR(2048) NULL,
      URLu NVARCHAR(2048) NULL,
      URL AS (ISNULL(CONVERT(NVARCHAR([URLa])), [URLu])),
      CONSTRAINT [CK_TableName_OneUrlMax] CHECK (
                        ([URLa] IS NOT NULL OR [URLu] IS NOT NULL)
                    AND ([URLa] IS NULL OR [URLu] IS NULL))
    );
    

    In this model you only SELECT from the [URL] computed column. For inserting and updating, you determine which field to use by seeing if converting alters the incoming value, which has to be of NVARCHAR type:

    INSERT INTO TableName (..., URLa, URLu)
    VALUES (...,
            IIF (CONVERT(VARCHAR(2048), @URL) = @URL, @URL, NULL),
            IIF (CONVERT(VARCHAR(2048), @URL) <> @URL, NULL, @URL)
           );
    
  3. If you have fields that should only ever have characters that fit into a particular Code Page of an Extended ASCII character set, then just use VARCHAR.


P.S. 为了明确说明:在SQL Server 2012中引入的新_SC排序规则仅允许:

  • 内置函数正确处理补充字符/代理对,以及
  • 用于排序和比较的补充字符的语言规则

但是,即使没有新的_SC排序规则,您仍然可以将任何Unicode字符存储到XML或N前缀类型中,并且检索时不会丢失数据。但是,当使用旧的排序规则(即名称中没有版本号时),所有补充字符都等同于彼此。您需要使用_90_100排序规则,它们至少可以进行二进制/代码点比较和排序;它们无法考虑语言规则,因为它们没有特定的补充字符映射(因此没有权重或规范化规则)。

请尝试以下操作:

IF (N'' = N'') SELECT N'' AS [TheLiteral], NCHAR(150150) AS [Generated];
IF (N'' = N'') SELECT N'' AS [TheLiteral], NCHAR(150151) AS [Generated];
IF (N'' COLLATE Tatar_90_CI_AI = N'' COLLATE Tatar_90_CI_AI)
       SELECT N' COLLATE Tatar_90_CI_AI' AS [TheLiteral], NCHAR(150151) AS [Generated];
IF (N'' = N'?') SELECT N'?';

在一个默认排序规则以 _SC 结尾的数据库中,只有第一个 IF 语句会返回结果集,并且 "Generated" 字段将正确显示字符。
但是,如果数据库没有默认排序规则以 _SC 结尾,并且排序规则不是 _90_100 系列排序规则,则前两个 IF 语句返回结果集,其中 "Generated" 字段将返回 NULL,而 "Literal" 字段将正确显示。
对于 Unicode 数据,排序规则对物理存储没有影响。

更新于2018年10月2日

虽然目前还不是可行的选择,但 SQL Server 2019 引入了对 VARCHAR / CHAR 数据类型的本地 UTF-8 支持。目前存在太多的错误,无法使用,但如果这些错误被修复,那么这是一些情况下的选项。请参阅我发布的文章 "SQL Server 2019 中的本地 UTF-8 支持:救世主还是伪先知?",详细分析此新功能。


NVARCHAR(1 - 4000) 是什么意思? - Eric J.
@EricJ。它意味着在1到4000之间选择一个数字。 - Aaron Bertrand
@EricJ。如果我没有表达清楚,对不起。基本上就是像Aaron所说的那样:这只是我的一种方式来表示非MAX NVARCHAR类型,它只能在1-4000的范围内。 - Solomon Rutzky

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接