Sql-Server

對於 CLR 函式輸入參數,使用 Strings 而不是 SqlStrings 是否安全?

  • May 20, 2019

我有一個通過 C# 程式碼實現的 CLR 標量 UDF。我注意到,與String數據類型相比,將數據類型用於輸入參數可顯著提高性能SqlString。在SQLCLR 第 5 級的階梯:開發(在 SQL Server 中使用 .NET)中,Solomon Rutzky提到了以下原因更喜歡字元串的 SQL 數據類型:

本機公共語言執行時 (CLR) 數據類型和 SQL Server 數據類型之間的主要區別在於前者不允許 NULL 值,而後者提供完整的 NULL 語義。

流式值可以通過 SqlChars for N 來實現

$$ VAR $$CHAR, SqlBytes 為$$ VAR $$BINARY,以及用於 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];
   }
}

很好的問題。據我所知,在這些條件下(即保證沒有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在未指定文化時使用的操作。
  2. 在某種程度上,排序規則影響的這三個級別都不相關,因為=操作員進行了序數比較(即應該與使用_BIN2排序規則相同)。這也是String.CompareOrdinal工作方式,以及傳入或值String.Equals時。StringComparison.CurrentCulture*``StringComparison.InvariantCulture*
  3. SQL Server 排序規則很重要的一個實例是將SqlString輸入參數與stringvia 連接時+。在這種情況下,+運算符創建一個 newSqlString來包含 的值,string以便它可以連接兩個SqlStrings。問題是 newSqlString是用目前執行緒的 LCID(也就是作業系統的 LCID)創建的,然後+操作符在連接之前比較這兩個SqlStringss(即驗證它們是“相同類型”)。但是,由於SqlString輸入參數具有數據庫的 LCID (不是實例或列)和隱式創建的SqlString具有作業系統的 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];
   }
}

此外,還有一些其他可能測試的選項:

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

引用自:https://dba.stackexchange.com/questions/238518