用于在 SQL Server 中计算组合字符的 CLR 函数

问题描述 投票:0回答:1

以下 C# 方法考虑组合字符(Grapheme Clusters)来计算字符串字符数。这是:

public static class StringExtensions
{
    public static SqlInt32 GetStrLength(this string input)
    {
        if (string.IsNullOrEmpty(input))
            return 0;

        return StringInfo.ParseCombiningCharacters(input).Length;
    }
}

现在,我从中创建一个 CLR 以在 SQL Server 内部使用。这是它的代码:

public static class UserDefinedFunctions
{
    [SqlFunction(IsDeterministic = true, IsPrecise = true)]
    public static SqlInt32 GetStrLength(SqlString input)
    {
        if (input.IsNull)
            return 0;

        return StringInfo.ParseCombiningCharacters(input.Value).Length;
    }
}

C# 版本运行良好,但在 SQL Server 中,无法正常计数。有什么问题吗?

以下是 SQLCLR 函数无法正确计数的几个示例:

SQLCLR 版本(错误) 非 SQLCLR 版本(正确)
'👩🏻' -> 2 '👩🏻' -> 1
'👨🏻u200d❤️u200d💋u200d👩🏼' -> 9 '👨🏻u200d❤️u200d💋u200d👩🏼' -> 1

这是我为获取长度而运行的 SQL 代码:

SELECT dbo.GetStringLength(body) FROM notes;

以下是用于注册SQLCLR的SQL代码:

EXEC sp_configure 'show advanced options' , 1;
RECONFIGURE;

EXEC sp_configure 'clr enable' ,1;
RECONFIGURE;

EXEC sp_configure 'clr strict security', 0;
RECONFIGURE;

CREATE ASSEMBLY StringUtils FROM 'C:\GraphemeClusters.dll' WITH PERMISSION_SET = SAFE;

CREATE FUNCTION dbo.GetStringLength(@input NVARCHAR(MAX))
RETURNS INT
AS EXTERNAL NAME StringUtils.UserDefinedFunctions.GetStrLength;
sql-server sqlclr grapheme-cluster
1个回答
0
投票

数据库的排序规则并不重要,因为

_SC
_140_
排序规则仅真正影响内置函数的行为,并且仅与增补字符相关。

我在 SQL Server 2017 和 2022 中使用以下测试字符串测试了您的

GetStrLength
方法以及
StringInfo.LengthInTextElements

DECLARE @Input NVARCHAR(50) =
    -- first two count individually as they do not have a base character
    NCHAR(0x0303) + NCHAR(0x0303) +
    -- next character counts as it is a base character
    N'o' +
    -- next four do not count as they all attach to the base character
    NCHAR(0x0303) + NCHAR(0x0302) + NCHAR(0x0303) + NCHAR(0x0302);

并且在所有情况下都返回了预期值 3(“0x0303”和“0x0302”都是组合字符)。

问题在于 SQL Server 与 .NET Framework 绑定在一起,并且最高版本 4.8 不处理表情符号“序列”。然而,较新版本的 .NET 可以。因此,虽然您认为正在针对 .NET Framework 4.8 测试非 SQLCLR 版本,但实际上您正在针对更新版本的 .NET 进行测试。

我在 LINQPad 版本 5 和 8 中执行了以下操作(OP 提供的测试表情符号序列,减去 U+1F3FB 的肤色修饰符,可以在 Unicode 表情符号文档 中找到,作为“多人”性别”示例):

string smooch =
"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FB";

System.Console.WriteLine(smooch);
System.Console.WriteLine(
    System.Globalization.StringInfo.ParseCombiningCharacters(smooch)
        .Length);

与 .NET Framework 4.7 和 4.8 配合使用的 LINQPad 5 返回:

👩🏻‍❤️‍💋‍👨🏻
9

与 .NET 5 到 9 兼容的 LINQPad 8 返回(我针对 .NET 6.0.35 和 8.0.10 进行了测试):

👩🏻‍❤️‍💋‍👨🏻
1

(稍后我将用每个表情符号的输出图片进行更新,因为虽然 LINQPad 8 的输出与上面看到的相同,但 LINQPad 5 的输出显示 4 个不同的表情符号——女人、心、嘴唇和男人—— - 使用他们的文本演示而不是单个字形)

© www.soinside.com 2019 - 2024. All rights reserved.