对 CLR 函数输入参数使用 Strings 而不是 SqlStrings 是否安全?

Joe*_*ish 6 sql-server best-practices datatypes sql-clr sql-server-2017

我有一个通过 C# 代码实现的 CLR 标量 UDF。我注意到,与String数据类型相比,将SqlString数据类型用于输入参数可以显着提高性能。在通往 SQLCLR 级别 5 的阶梯:开发(在 SQL Server 中使用 .NET)中Solomon Rutzky提到了以下原因更喜欢字符串的 SQL 数据类型:

本机公共语言运行时 (CLR) 数据类型和 SQL Server 数据类型之间的主要区别在于,前者不允许 NULL 值,而后者提供完整的 NULL 语义。

...

可以通过 N[VAR]CHAR 的 SqlChars、[VAR]BINARY 的 SqlBytes 和 XML 的 SqlXml.CreateReader() 来实现流值...

...

使用 SqlString(不是字符串,甚至不是 SqlChars)时,您可以访问 CompareInfo、CultureInfo、LCID 和 SqlCompareOptions 属性...

我知道我的输入永远不会为 NULL,我不需要将值传入,并且我永远不会检查排序规则属性。我的情况可能是一个例外,最好使用String而不是SqlString?如果我确实采用这种方法,有什么需要特别注意的吗?

如果重要的话,我正在使用 SQL Server 的默认排序规则。这是我的源代码的一部分,s1作为输入参数:

fixed (char* chptr = s1)
{
    char* cp = (char*)current;

    for (int i = 0; i < s1.Length; i++)
    {
        cp[i] = chptr[i];
    }
}
Run Code Online (Sandbox Code Playgroud)

Sol*_*zky 6

很好的问题。据我所知,在这些条件下(即保证没有NULLs 并且不需要额外的功能)不应该有任何具体的问题。这可能是一种类似于CURSORs的情况,如果需要通用规则,它将是:“不要使用游标”。但是,实际规则是:“仅在适当的时候/在适当的地方使用游标”。问题是教育人们了解游标的技术细节,以便他们可以做出决定,我们这些对此类事情足够了解的人会忽略通用规则并继续适当地使用它们。

因此,我建议人们“始终”使用这些Sql*类型,因为它可以减少混淆和错误。但是,这并不是说string在您的情况下使用不会更好。我说去吧,如果你遇到了问题string,很容易回去把它改成SqlString.

关于整理和您的声明:

如果重要的话,我正在使用 SQL Server 的默认排序规则。

虽然这通常无关紧要,但鉴于没有真正的默认排序规则,您在这里的意思也有点不清楚。在语言设置为“美国英语”(即 LCID = 1033)的操作系统上安装 SQL Server 时,您可能指的是不幸的默认排序规则,SQL_Latin1_General_CP1_CI_AS. 但是仍然有三个级别的排序规则都可以不同(实例/服务器、数据库和列),您可能只表示这些级别中的一个或什至两个。

我提到所有这些的原因是这里发生了一些不明显的事情:

  1. 在某种程度上,排序规则影响的这 3 个级别中没有一个是相关的,因为 SQLCLR 线程的默认区域性是操作系统级别的语言设置(所选语言的 LCID)。这会影响String.Equals使用这两个StringComparison.CurrentCulture*值之一String.Compare时的操作 using 以及未指定区域性时使用的操作。

  2. 在某种程度上,排序规则影响的这 3 个级别中=没有一个是相关的,因为操作员进行顺序比较(即应该与使用_BIN2排序规则相同)。这也是如何String.CompareOrdinal工作,以及String.Equals传递StringComparison.CurrentCulture*StringComparison.InvariantCulture*值。

  3. SQL Server 排序规则很重要的一个实例是将SqlString输入参数与stringvia 连接+。在这种情况下,+运算符会创建一个 newSqlString来包含 的值,string以便它可以连接两个SqlStrings。问题是 newSqlString是用当前线程 LCID(这是操作系统的 LCID)创建的,然后+运算符SqlStrings在串联之前比较这两个s(即验证它们是“相同类型”)。但是,由于SqlString输入参数的数据库(不是实例或列)和隐式创建的SqlString的 LCID 具有操作系统的 LCID,因此该操作会出现异常,指出“排序规则”不匹配。不错,嗯?

    但是,这应该不是问题,因为SqlString在需要字符串时,没有人应该直接使用该值。每个人都应该始终使用该Value属性来获取字符串。


话虽如此,我很好奇你做了什么测试来确定它string更快。我测试了一个简单的 UDF,它接受单个NVARCHAR(4000)输入参数,连接一个短字符串,然后返回新值。该 UDF 的一个版本接受并返回string,而另一个版本接受并返回SqlString。超过 100 万次迭代,string版本比SqlString版本快大约 200-300 毫秒,大约 50% 的时间,当比较它们的最快时间时(在所有 100 万次迭代中,而不是每个迭代)。其他 50% 的时间性能提升约为 100 毫秒,但也可能没有。

另外,关于您的测试代码:s1始终是直接输入参数,无论是string还是SqlString? 如果是,那么您还应该测试在本地创建字符串并设置它s1.Value。意义:

string s2 = s1.Value; // when s1 is SqlString instead of string

fixed (char* chptr = s2)
{
    char* cp = (char*)current;

    for (int i = 0; i < s2.Length; i++)
    {
        cp[i] = chptr[i];
    }
}
Run Code Online (Sandbox Code Playgroud)

此外,还有一些其他可能测试的选项:

  1. SqlString.GetUnicodeBytes方法(返回byte[]
  2. SqlChars.Value属性(返回char[]