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 表示的任何字符串。那是对的吗?这种策略是否有缺点(例如,为每一行执行托管代码的性能成本,...)?

4

1 回答 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)or 或VARBINARY(MAX)NVARCHAR(1 - 4000)andVARBINARY(1 - 4000)相对时(请不要将此区别混淆为暗示使用SqlChars/ SqlBytesvs SqlString/ SqlBinary)。

最后(至少在使用 UDT 方面),请不要忽略被查询的 UDT 是示例代码这一事实。唯一提到的测试纯粹是功能性的,与可伸缩性或“使用一年后的经验教训”无关。功能测试代码显示在下面的 CodePlex 页面中,在继续此决定之前应该先查看它,因为它可以让您了解您需要如何编写查询才能与之交互(这对于字段或两个,但不适用于大多数/所有字符串字段):

http://msftengprodsamples.codeplex.com/SourceControl/latest#Kilimanjaro_Trunk/Programmability/CLR/UTF8String/Scripts/Test.sql

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


如果需要考虑空间(磁盘、内存等),您有以下三种选择:

  1. 如果您使用的是 SQL Server 2008 或更高版本,并且在 Enterprise Edition 上,则可以启用Data Compression。数据压缩可以(但不会“总是”)压缩NCHARNVARCHAR字段中的 Unicode 数据。决定因素是:

    1. NCHAR(1 - 4000)NVARCHAR(1 - 4000)使用Unicode 的标准压缩方案,但仅从 SQL Server 2008 R2 开始,并且仅用于 IN ROW 数据,而不是 OVERFLOW!这似乎比常规的 ROW / PAGE 压缩算法要好。
    2. NVARCHAR(MAX)并且XML(我猜还有VARBINARY(MAX),TEXTNTEXT)IN ROW 数据(不在 LOB 或 OVERFLOW 页面中的行外)至少可以进行 PAGE 压缩,也可以进行ROW 压缩(不确定最后一个)。
    3. 任何 OFF ROW 数据、LOB 或 OVERLOW = 无压缩!
  2. 如果在 Enterprise Edition 上使用早于 2008 的版本,则可以有两个字段: oneVARCHAR和 one NVARCHAR。例如,假设您存储的 URL 大多都是基本 ASCII 字符(值 0 - 127),因此适合VARCHAR,但有时包含 Unicode 字符。您的架构可以包含以下 3 个字段:

      ...
      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))
    );
    

    在此模型中,您只能[URL]计算列中选择。对于插入和更新,您可以通过查看转换是否更改传入值来确定要使用的字段,该值必须是以下NVARCHAR类型:

    INSERT INTO TableName (..., URLa, URLu)
    VALUES (...,
            IIF (CONVERT(VARCHAR(2048), @URL) = @URL, @URL, NULL),
            IIF (CONVERT(VARCHAR(2048), @URL) <> @URL, NULL, @URL)
           );
    
  3. 如果您的字段应该只包含适合扩展 ASCII 字符集的特定代码页的字符,那么只需使用VARCHAR.


PS 只是为了清楚起见:_SCSQL Server 2012 中引入的新排序规则只允许:

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

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

尝试以下操作:

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条语句将返回结果集,并且“生成”字段将正确显示字符。

但是,如果数据库没有以 结尾的默认排序规则_SC,并且排序规则不是一个_90_100系列排序规则,那么前两个IF语句将返回结果集,其中“生成”字段将返回NULL,并且“文字”字段正确显示.

对于 Unicode 数据,排序规则与物理存储无关。


更新 2018-10-02

虽然这还不是一个可行的选项,但 SQL Server 2019 在VARCHAR/CHAR数据类型中引入了对 UTF-8 的本机支持。目前它有太多的错误无法使用,但如果它们被修复,那么这是某些场景的一个选项。请参阅我的帖子“ SQL Server 2019 中的 Native UTF-8 Support: Savior or False Prophet? ”,详细分析这个新特性。

于 2015-09-27T07:03:36.403 回答