SQL Server中CLR Grapheme簇计数函数失效问题排查
现有一段C#方法可正确统计包含组合字符的Grapheme Clusters(grapheme簇)数量,代码如下:
public static class StringExtensions { public static SqlInt32 GetStrLength(this string input) { if (string.IsNullOrEmpty(input)) return 0; return StringInfo.ParseCombiningCharacters(input).Length; } }
将其封装为SQL Server可用的CLR函数后,代码如下:
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#方法在非SQLCLR环境运行正常,但在SQL Server中使用CLR函数时计数结果错误,示例如下:
| SQLCLR版本(错误) | 非SQLCLR版本(正确) |
|---|---|
| '👩🏻' -> 2 | '👩🏻' -> 1 |
| '👨🏻❤️💋👩🏼' -> 9 | '👨🏻❤️💋👩🏼' -> 1 |
调用该CLR函数的SQL代码:
SELECT dbo.GetStringLength(body) FROM notes;
注册CLR的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;
.NET Runtime版本的Unicode支持差异:
SQL Server的CLR集成默认使用.NET Framework(即使是SQL Server 2019及以后版本,默认仍采用.NET Framework),而.NET Framework的StringInfo.ParseCombiningCharacters基于较旧的Unicode标准(例如.NET Framework 4.8对应Unicode 11.0),无法识别零宽连接符(U+200D)连接的复杂emoji序列(如👨🏻❤️💋👩🏼),也无法正确处理部分新的组合字符簇。而非SQLCLR测试版本大概率使用的是.NET Core/.NET 5+,这些版本的StringInfo实现更新到了更高版本的Unicode标准,能正确识别这类复杂grapheme簇。组合字符的识别逻辑差异:
对于带肤色修饰符的单个emoji(如👩🏻),.NET Framework的StringInfo未将基础emoji字符与肤色修饰符视为一个完整的grapheme簇,这是因为旧版本的Unicode分类规则未被StringInfo正确适配,而新版本的.NET Runtime修复了该问题。
内容的提问来源于stack exchange,提问作者MJDevelops

