3

研究SQL Server的VARCHAR與NVARCHAR的pro和con是否適合我的特定應用程序,我認識到如果SQL Server本身支持UTF8,那將是理想選擇。幾個SO帖子表明它沒有,例如:UTF-8支持,SQL Server 2012和UTF8String UDT

Is VARCHAR like totally 1990s?

What are the main performance differences between varchar and nvarchar SQL Server data types?

但是後來我的MSDN文檔爲SQL Server 2012,演示如何創建一個UTF8字符串用戶定義的跨這篇文章來數據類型:

http://msdn.microsoft.com/en-us/library/ff877964(v=sql.110).aspx

似乎UDT將允許空間(存儲器,磁盤)的8位每字符的好處,同時足夠靈活以存儲任何可以用UTF-8表示的字符串。那是對的嗎?這種策略是否有缺點(例如,對每行執行託管代碼的性能成本......)?

回答

2

通過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 vs SqlString/SqlBinary)。

最後(至少在使用UDT方面),請不要看看被查詢的UDT是示例代碼這一事實。唯一指出的測試純粹是功能性的,沒有任何可擴展性或「在使用這一年後獲得的經驗教訓」。功能測試代碼顯示在下面的CodePlex頁面中,在繼續這個決定之前應該看看它,因爲它讓你瞭解瞭如何編寫查詢以便與它進行交互(這適用於字段或二,但大多數/所有字符串字段):

http://msftengprodsamples.codeplex.com/SourceControl/latest#Kilimanjaro_Trunk/Programmability/CLR/UTF8String/Scripts/Test.sql

鑑於持久化計算列和索引增加的號碼,在任何空間真的救了? ;-)


如果空間(磁盤,內存等)的關注,你有三種選擇:

  1. 如果您在使用SQL Server 2008或更高版本,並在企業版,那麼你可以啓用Data Compression。數據壓縮可以(但不會「始終」)壓縮NCHARNVARCHAR字段中的Unicode數據。的決定因素是:

    1. NCHAR(1 - 4000)NVARCHAR(1 - 4000)使用Standard Compression Scheme for Unicode,但只在SQL Server 2008 R2開始,的,只能用於行數據,不溢出!這似乎比常規的ROW/PAGE壓縮算法更好。
    2. NVARCHAR(MAX)XML(我也想VARBINARY(MAX)TEXT,並NTEXT)數據是IN行(LOB或溢出頁不脫行)可以至少PAGE壓縮,也許還行壓縮(不知道關於這最後一個)。
    3. 任何OFF ROW數據,LOB或OVERLOW =無壓縮!
  2. 如果使用的不是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)) 
    ); 
    

    在這種模式下,你只SELECT[URL]計算列。對於插入和更新,您確定通過查看使用哪個領域,如果將改變傳入的值,它必須是NVARCHAR類型:

    INSERT INTO TableName (..., URLa, URLu) 
    VALUES (..., 
         IIF (CONVERT(VARCHAR(2048), @URL) = @URL, @URL, NULL), 
         IIF (CONVERT(VARCHAR(2048), @URL) <> @URL, NULL, @URL) 
         ); 
    
  3. 如果您有字段應該永遠只能有一個適應一個字符特定代碼頁的擴展ASCII字符集,然後只使用VARCHAR


附:只是有這個規定爲清楚:在SQL Server 2012中引入的新_SC排序規則簡單地允許:

  • 內置的功能,妥善處理增補字符/代理人對,
  • 語言規則對於那些用於排序增補字符和比較

但是,即使沒有新的_SC排序規則,你仍然可以任何Unicode字符存儲到一個XML或N -prefixed類型,並且不會丟失數據檢索。但是,當使用較舊的Columns(即名稱中沒有版本號)時,所有補充字符都相互等同。您需要使用_90_100排序規則,它們至少可以幫助您進行二進制/代碼點比較和排序;他們不能考慮語言規則,因爲他們沒有特殊的補充字符映射(因此沒有權重或規範化規則)。

嘗試以下操作:

IF (N'' = N'') SELECT N'' AS [TheLiteral], NCHAR(150150) AS [Generated]; 
IF (N'' = N'') SELECT N'' AS [TheLiteral], NCHAR(150151) AS [Generated]; 
IF (N'' COLLATE Tatar_90_CI_AI = N'' COLLATE Tatar_90_CI_AI) 
     SELECT N' COLLATE Tatar_90_CI_AI' AS [TheLiteral], NCHAR(150151) AS [Generated]; 
IF (N'' = N'?') SELECT N'?'; 

在DB具有_SC結尾的默認排序規則,只有第一IF語句將返回結果集,而「生成」字段將正確顯示的字符。

但是,如果數據庫沒有缺省歸類在_SC結束,且覈對是不是_90_100系列歸類,則前兩個IF語句返回結果集,其中的「生成」區域將返回NULL,和「文字」字段正確顯示。

對於Unicode數據,排序規則對物理存儲沒有影響。

+0

'NVARCHAR(1 - 4000)'是什麼意思? –

+0

@EricJ。這意味着選擇1至4000之間的數字。 –

+0

@EricJ。對不起,如果我不清楚。基本上,Aaron說過:這只是我指示非'MAX' NVARCHAR類型的方式,它只能在1 - 4000的範圍內。 –