本文介紹了UTF-8支持、SQL Server 2012和UTF8字符串UDT的處理方法,對大家解決問題具有一定的參考價值,需要的朋友們下面隨著小編來一起學習吧!
問題描述
在研究針對我的特定應用程序的SQL Server的VARCHAR和NVARCHAR的優缺點時,我意識到如果SQL Server本機支持UTF8將是最理想的。一些SO帖子表明它不是這樣的,例如:
Is VARCHAR like totally 1990s?
What are the main performance differences between varchar and nvarchar SQL Server data types?
然而,后來我在SQL Server 2012的MSDN文檔中看到了這篇文章,其中介紹了如何創建UTF8字符串用戶定義的數據類型:
http://msdn.microsoft.com/en-us/library/ff877964(v=sql.110).aspx
UDT似乎允許每個字符8位的空間(內存、磁盤)優勢,同時足夠靈活地存儲可以用UTF-8表示的任何字符串。對嗎?此策略是否有不足之處(例如,為每行執行托管代碼的性能成本…)?
SQLCLR
通過推薦答案創建自定義用戶定義類型是而不是,無論如何,它都會為您帶來任何本機類型的替換。對于創建處理特定數據的東西來說,它非常方便。但是,即使是不同編碼的字符串,也遠遠不是專門化的。使用此方法獲取字符串數據將破壞系統的任何可用性,更不用說性能了,因為您將無法使用任何內置字符串函數。
如果您能夠在磁盤空間上節省任何東西,那么這些收益將被您在總體性能上的損失所抹去。存儲UDT的方法是將其序列化為VARBINARY
。因此,為了進行任何字符串比較或排序,在”二進制”/”序數”比較之外,您必須將所有其他值逐個轉換回UTF-8,然后進行可以考慮語言差異的字符串比較。而這一轉換需要在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
vsSqlString
/SqlBinary
)。
最后(至少就使用UDT而言),請不要忽視所查詢的UDT是示例代碼這一事實。唯一提到的測試是純粹的功能性測試,不是關于可伸縮性的,也不是”使用一年后學到的教訓”。下面的CodePlex頁面顯示了功能測試代碼,在繼續進行此決策之前應先查看該代碼,因為它使您了解需要如何編寫查詢才能與其交互(對于一兩個字段是可以的,但對于大多數/所有字符串字段不是):
http://msftengprodsamples.codeplex.com/SourceControl/latest#Kilimanjaro_Trunk/Programmability/CLR/UTF8String/Scripts/Test.sql
考慮到添加的持久化計算列和索引的數量,是否真的節省了空間?;-)
考慮空間(磁盤、內存等)的情況下,您有三種選擇:
如果您使用的是SQL Server 2008或更高版本,并且使用的是Enterprise Edition,則可以啟用Data Compression。數據壓縮可以(但不會總是)壓縮NCHAR
和NVARCHAR
字段中的Unicode數據。決定因素是:
NCHAR(1 - 4000)
和NVARCHAR(1 - 4000)
使用Standard Compression Scheme for Unicode,但僅從SQL Server2008 R2開始,且僅用于IN行數據,不能溢出!這似乎比常規的行/頁壓縮算法要好。NVARCHAR(MAX)
和XML
(我猜還包括VARBINARY(MAX)
、TEXT
和NTEXT
)在行中的數據(不在LOB或溢出頁中的行外)至少可以進行頁面壓縮,可能也可以進行行壓縮(不確定最后一個)。任何行外數據、LOB或OVERLOW=無需壓縮!
如果在Enterprise Edition上使用的版本早于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'