通过 SQLCLR 创建自定义的用户定义类型不是,无论如何,它会让您替换任何本机类型。创建一些东西来处理专门的数据非常方便。但是字符串,即使是不同的编码,也远非专业化的。为您的字符串数据采用这条路线会破坏系统的任何可用性,更不用说性能,因为您将无法使用 任何 内置字符串函数。
如果您能够在磁盘空间上节省任何东西,那么这些收益将被您在整体性能方面的损失所抵消。通过将 UDT 序列化为 VARBINARY 来存储 UDT。因此,为了进行 any 字符串比较或排序,在“二进制”/“序数”比较之外,您必须将所有其他值一一转换回 UTF-8 到 then进行可以解释语言差异的字符串比较。这种转换需要在 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 / SqlBytes vs SqlString / SqlBinary)。
最后(至少在使用 UDT 方面),请不要忽略所查询的 UDT 是示例代码这一事实。唯一提到的测试纯粹是功能性的,与可伸缩性或“使用一年后的经验教训”无关。功能测试代码显示在下面的 CodePlex 页面中,在继续此决定之前应该先查看它,因为它可以让您了解您需要如何编写查询以便与之交互(这对于字段或两个,但对于大多数/所有字符串字段不是):
http://msftengprodsamples.codeplex.com/SourceControl/latest#Kilimanjaro_Trunk/Programmability/CLR/UTF8String/Scripts/Test.sql
考虑到添加的持久计算列和索引的数量,是否真的节省了空间? ;-)
如果需要考虑空间(磁盘、内存等),您有三个选择:
-
如果您使用的是 SQL Server 2008 或更高版本,并且使用的是企业版,那么您可以启用 Data Compression。数据压缩可以(但不会“总是”)压缩NCHAR 和NVARCHAR 字段中的Unicode 数据。决定因素是:
-
NCHAR(1 - 4000) 和 NVARCHAR(1 - 4000) 使用 Standard Compression Scheme for Unicode,但仅从 SQL Server 2008 R2 开始,并且仅用于 IN ROW 数据,而不是 OVERFLOW!这似乎比常规的 ROW / PAGE 压缩算法要好。
-
NVARCHAR(MAX) 和 XML(我猜还有 VARBINARY(MAX)、TEXT 和 NTEXT)IN ROW 数据(不在 LOB 或 OVERFLOW 页面中的行外)至少可以进行 PAGE 压缩,并且 也许还有 ROW 压缩(不确定最后一个)。
- 任何 OFF ROW 数据、LOB 或 OVERLOW = 无需压缩!
-
如果在企业版上使用早于 2008 年的版本,您可以有两个字段:一个 VARCHAR 和一个 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)
);
如果您的字段应该只包含适合扩展 ASCII 字符集的特定代码页的字符,那么只需使用 VARCHAR。
附:只是为了清楚起见: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 语句将返回结果集,并且“生成”字段将正确显示字符。
但是,如果数据库没有以 _SC 结尾的默认排序规则,并且排序规则不是 _90 或 _100 系列排序规则,则前两个 IF 语句返回结果集,其中“ Generated”字段将返回NULL,“Literal”字段正确显示。
对于 Unicode 数据,排序规则与物理存储无关。
2018 年 10 月 2 日更新
虽然这还不是一个可行的选项,但 SQL Server 2019 在 VARCHAR / CHAR 数据类型中引入了对 UTF-8 的原生支持。目前它有太多的错误无法使用,但如果它们被修复,那么这是 一些 场景的一个选项。有关此新功能的详细分析,请参阅我的帖子“Native UTF-8 Support in SQL Server 2019: Savior or False Prophet?”。