本文介紹了UTF-8支持、SQL Server 2012和UTF8字符串UDT的處理方法,對(duì)大家解決問(wèn)題具有一定的參考價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧!
問(wèn)題描述
在研究針對(duì)我的特定應(yīng)用程序的SQL Server的VARCHAR和NVARCHAR的優(yōu)缺點(diǎn)時(shí),我意識(shí)到如果SQL Server本機(jī)支持UTF8將是最理想的。一些SO帖子表明它不是這樣的,例如:
Is VARCHAR like totally 1990s?
What are the main performance differences between varchar and nvarchar SQL Server data types?
然而,后來(lái)我在SQL Server 2012的MSDN文檔中看到了這篇文章,其中介紹了如何創(chuàng)建UTF8字符串用戶定義的數(shù)據(jù)類型:
http://msdn.microsoft.com/en-us/library/ff877964(v=sql.110).aspx
UDT似乎允許每個(gè)字符8位的空間(內(nèi)存、磁盤)優(yōu)勢(shì),同時(shí)足夠靈活地存儲(chǔ)可以用UTF-8表示的任何字符串。對(duì)嗎?此策略是否有不足之處(例如,為每行執(zhí)行托管代碼的性能成本…)?
SQLCLR
通過(guò)推薦答案創(chuàng)建自定義用戶定義類型是而不是,無(wú)論如何,它都會(huì)為您帶來(lái)任何本機(jī)類型的替換。對(duì)于創(chuàng)建處理特定數(shù)據(jù)的東西來(lái)說(shuō),它非常方便。但是,即使是不同編碼的字符串,也遠(yuǎn)遠(yuǎn)不是專門化的。使用此方法獲取字符串?dāng)?shù)據(jù)將破壞系統(tǒng)的任何可用性,更不用說(shuō)性能了,因?yàn)槟鷮o(wú)法使用任何內(nèi)置字符串函數(shù)。
如果您能夠在磁盤空間上節(jié)省任何東西,那么這些收益將被您在總體性能上的損失所抹去。存儲(chǔ)UDT的方法是將其序列化為VARBINARY
。因此,為了進(jìn)行任何字符串比較或排序,在”二進(jìn)制”/”序數(shù)”比較之外,您必須將所有其他值逐個(gè)轉(zhuǎn)換回UTF-8,然后進(jìn)行可以考慮語(yǔ)言差異的字符串比較。而這一轉(zhuǎn)換需要在UDT內(nèi)完成。這意味著,與XML數(shù)據(jù)類型一樣,您將創(chuàng)建UDT以保存特定值,然后公開(kāi)該UDT的方法以接受字符串參數(shù)進(jìn)行比較(即Utf8String.Compare(alias.field1)
,或者,如果為該類型定義運(yùn)算符,則Utf8string1 = Utf8string2
并使=
運(yùn)算符獲取UTF-8編碼的字符串,然后執(zhí)行CompareInfo.Compare()
)。
除了上述注意事項(xiàng)外,您還需要考慮通過(guò)SQLCLR API來(lái)回傳送值是有代價(jià)的,特別是在使用NVARCHAR(MAX)
或VARBINARY(MAX)
而不是分別使用NVARCHAR(1 - 4000)
和VARBINARY(1 - 4000)
時(shí)(請(qǐng)不要將這種區(qū)別混淆為使用SqlChars
/SqlBytes
vsSqlString
/SqlBinary
)。
最后(至少就使用UDT而言),請(qǐng)不要忽視所查詢的UDT是示例代碼這一事實(shí)。唯一提到的測(cè)試是純粹的功能性測(cè)試,不是關(guān)于可伸縮性的,也不是”使用一年后學(xué)到的教訓(xùn)”。下面的CodePlex頁(yè)面顯示了功能測(cè)試代碼,在繼續(xù)進(jìn)行此決策之前應(yīng)先查看該代碼,因?yàn)樗鼓私庑枰绾尉帉?xiě)查詢才能與其交互(對(duì)于一兩個(gè)字段是可以的,但對(duì)于大多數(shù)/所有字符串字段不是):
http://msftengprodsamples.codeplex.com/SourceControl/latest#Kilimanjaro_Trunk/Programmability/CLR/UTF8String/Scripts/Test.sql
考慮到添加的持久化計(jì)算列和索引的數(shù)量,是否真的節(jié)省了空間?;-)
考慮空間(磁盤、內(nèi)存等)的情況下,您有三種選擇:
如果您使用的是SQL Server 2008或更高版本,并且使用的是Enterprise Edition,則可以啟用Data Compression。數(shù)據(jù)壓縮可以(但不會(huì)總是)壓縮NCHAR
和NVARCHAR
字段中的Unicode數(shù)據(jù)。決定因素是:
NCHAR(1 - 4000)
和NVARCHAR(1 - 4000)
使用Standard Compression Scheme for Unicode,但僅從SQL Server2008 R2開(kāi)始,且僅用于IN行數(shù)據(jù),不能溢出!這似乎比常規(guī)的行/頁(yè)壓縮算法要好。NVARCHAR(MAX)
和XML
(我猜還包括VARBINARY(MAX)
、TEXT
和NTEXT
)在行中的數(shù)據(jù)(不在LOB或溢出頁(yè)中的行外)至少可以進(jìn)行頁(yè)面壓縮,可能也可以進(jìn)行行壓縮(不確定最后一個(gè))。任何行外數(shù)據(jù)、LOB或OVERLOW=無(wú)需壓縮!
如果在Enterprise Edition上使用的版本早于2008或不是,您可以有兩個(gè)字段:一個(gè)VARCHAR
和一個(gè)NVARCHAR
。例如,假設(shè)您存儲(chǔ)的URL大多都是基本ASCII字符(值0-127),因此適合VARCHAR
,但有時(shí)包含Unicode字符。您的架構(gòu)可以包括以下3個(gè)字段:
...
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]
計(jì)算列中選擇。對(duì)于插入和更新,您可以通過(guò)查看轉(zhuǎn)換是否會(huì)改變傳入的值來(lái)確定要使用的字段,該值必須是NVARCHAR
類型:
INSERT INTO TableName (..., URLa, URLu)
VALUES (...,
IIF (CONVERT(VARCHAR(2048), @URL) = @URL, @URL, NULL),
IIF (CONVERT(VARCHAR(2048), @URL) <> @URL, NULL, @URL)
);
如果您的字段只應(yīng)包含適合擴(kuò)展ASCII字符集的特定代碼頁(yè)的字符,則只需使用VARCHAR
。
附注:僅為清楚起見(jiàn):SQL Server 2012中引入的新_SC
排序規(guī)則僅允許:
正確處理補(bǔ)充字符/代理項(xiàng)對(duì)的內(nèi)置函數(shù),以及
用于排序和比較的補(bǔ)充字符的語(yǔ)言規(guī)則
但是,即使沒(méi)有新的_SC
排序規(guī)則,您仍然可以將任何Unicode字符存儲(chǔ)為XML或N
前綴類型,并在不丟失數(shù)據(jù)的情況下檢索它。但是,當(dāng)使用較舊的歸類時(shí)(即名稱中沒(méi)有版本號(hào)),所有補(bǔ)充字符彼此相等。您需要使用_90
和_100
歸類,它們至少可以進(jìn)行二進(jìn)制/碼位比較和排序;它們不能考慮語(yǔ)言規(guī)則,因?yàn)樗鼈儧](méi)有補(bǔ)充字符的特定映射(因此沒(méi)有權(quán)重或標(biāo)準(zhǔn)化規(guī)則)。
嘗試以下操作:
IF (N'