2009年6月30日

探索 SqlCommand.Parameters.Add 定義資料值長度

其實這個問題也是老早之前就遇到 (大概一年半前吧...)
所以這兩天坎尼又重新測了一下問題發生的原因

先講一下這次的問題所在
SqlCommand.Parameters.Add 有個多載是可以傳入參數值的 size
但在 varchar 的狀態下,傳入中文並不會把值鎖定在 10,以至於系統拋出了例外

先來看範例,首先是資料表定義,有個 varchar(10) 的欄位

接著是可正常運作的範例

相信大家都知道,中文字在 varchar 中會佔 2 單位
所以6個中文字以上的字串,是無法新增至上面所定義的欄位表


當時坎尼就是遇到要把中文字存到 varchar 的欄位中
但很明顯的,size參數完全起不了作用
而且同事還說要不要用 String.SubString 的方法把資料截斷啊?

年幼的坎尼還真的照做了,但問題依舊存在
正確的做法應該是要先轉成 byte[] ,再將資料截斷送入

原本的 "無敵鐵金鋼2" 就被截斷後再送入資料庫中

但這個解法只限於傳入的值皆為 2bytes 的字
一但需截斷的資料中有1byte的字,就可能發生中文字被腰斬的情況



上面談了這麼多
是不是要證明 SqlCommand.Parameters.Add 的第3個 size 參數沒有用呢?
當然不是,它當然有用,只是在上面範例中不適合
其實只要把字串改成全為英文,Parameter就會自動把超過的字串切掉



上述例子中沒有作用的原因為:
中文字才6個,還未達到Length=10的瓶頸
但又因為中文字每個字代表2bytes,所以早就超過資料表裡的限制
中華文化博大精深,一般人尚無法精通,何況電腦

字串切割會以字的數目,而非字所佔的容量
「一二3四五」和「12345」同樣是算 5 個字
用 String.Substring(0,6) 即會拋出 index 不符的例外

但其實上述的範例真正的解決方式為...
應將有可能出現中文字的欄位設為 nvarchar,且前端要針對輸入的字作控管

最後補充一下小小的發現
上面 Table 裡的 ID 值是用遞增方式,但卻少了 2,難道是坎尼自己偷刪嗎?
其實是上述的測試中,坎尼故意傳了大於欄位長度的值進去,因此在 Sql 方面出現例外
不過遞增值的 flag 似乎自動加一了? 所以才會跳過該數字

3 則留言:

Unknown 提到...

用暴力法直接把超過長度的資料截斷,這感覺也不太對耶,我還以為超過 size 的話會丟出 exception 或者警告訊息?還是會留下 log?如果是默默把資料的尾巴去掉,那麼存到 datastore 中的資料就不完整了(資料遺失) ...

坎尼 提到...

所以說要用 nvarchar 的格式才對
但那時主系統不是我們做的,我們沒有權力修改資料表,反應上去也一直沒回音
才只能在 AP 上動手腳,當然截斷之前會令使用者知道可能被截斷的訊息

Unknown 提到...

剛剛再認真看了一下 MSDN (http://msdn.microsoft.com/en-us/library/40959t6x.aspx), 這說明還真是薄弱阿 ... 不過用 nvarchar 來存 unicode 的資料是最好的作法,至少以後要做資料轉檔的時候可以少處理一個編碼問題

Google Spreadsheet 裡用規則運算式

最近因為工作關係,遇到要用 Google Form 及 Google Sheet 所以研究了 Google Sheet 裡的一些 function 怎麼用 首先,分享一下如何在 Google Sheet 裡用規則運算 :D