所以這兩天坎尼又重新測了一下問題發生的原因
先講一下這次的問題所在
SqlCommand.Parameters.Add 有個多載是可以傳入參數值的 size
但在 varchar 的狀態下,傳入中文並不會把值鎖定在 10,以至於系統拋出了例外
先來看範例,首先是資料表定義,有個 varchar(10) 的欄位
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjp53lmptpEJTS_bTZSjCpXHQUJCUmgiGjMHG7TnEOyZgbgHIxDrB3YackfDGiHwqIitx8JiDQ4_7K0YGuWjWGZCPqcK1B6DpwdITYNUEVO8M6U1GzH_XBNSZtu_cCEis1Lwub3LzYPhfmK/s400/spb1.png)
接著是可正常運作的範例
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJswTfQyUcKvrFBtZ-vRIqz14JnWoXKgJ1WOChgLnq2EQXrmkFXEeNkxakCoAu2uq2-nYGvfGKa88AWaGYyz7xyJRfjcTKU0LdthAj4jKY7Ewfkgvlnkvy3c7VrQbOQVwt7HDAe_h-dzJU/s400/spb2.png)
相信大家都知道,中文字在 varchar 中會佔 2 單位
所以6個中文字以上的字串,是無法新增至上面所定義的欄位表
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjh9C6IABPGfKdGy9D2wPvX5G28np5_AxHqN-ft5Vnl5z9_AqoqMomL8St3JZE1PQWKo-xMKqyKxFVHzceBPBEy5Ve3vEszHn27UEvqzjD8nDMuE42ps0T8K-sr1gL6S7DekgpE69dd1nuE/s400/spb3.png)
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjK3GYn5TaQZYnLxJ_ozWYK19lTLMdiY8AyYLtbbKa0n4np4sjZs8H1lOyP_d4TFkXXBGxl3kd7UqPWsghFLNzpEiFl2gC_yPw719y0a4n3q-G-Kfa3TMAFfyXfviyhAlI3oOA9yBo22z4U/s400/spb4.png)
當時坎尼就是遇到要把中文字存到 varchar 的欄位中
但很明顯的,size參數完全起不了作用
而且同事還說要不要用 String.SubString 的方法把資料截斷啊?
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzMvOtnEZeR6HQSLnyTWnY8VSNKdjb7RjxZgzfPSzsMFdJjh1mqjjHjwNRmch4__4XPmAONGVtiK8pZDs_azS7Pdq0LsaGEIvD4zR9DWLU6fx68rVpwGwuixcWC-RW81HML56lk_5mdgLr/s400/spb5.png)
正確的做法應該是要先轉成 byte[] ,再將資料截斷送入
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6ppjIr6lFp-RYq8PQsqZx27YqsJlQBiDRK5roNrAvOmFjwQDrYOfu_3pOIjng2qQVDb96bI3W68kM15Y1dWhIYfNPxQLTmKoR_dmh-QMF7-iezqJlZsv_xCVTELvFhvMZTPZxxNUm5Nvw/s400/spb6.png)
原本的 "無敵鐵金鋼2" 就被截斷後再送入資料庫中
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZsJ7XJiIY2MztwNbv_j_cFdw75b0QRW_heD0wf1F_RDrsZGtalgxRrsUkY-LEe1wumoHPP9H3uYK1GhoFUM_vlG_8VTXZ20DXnDkmgvaOeeIe4Z_zPMyxiaB3ppj8f28LxtS1WIO6zJFz/s400/spb7.png)
但這個解法只限於傳入的值皆為 2bytes 的字
一但需截斷的資料中有1byte的字,就可能發生中文字被腰斬的情況
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIgwjE1qm2lUYmZwDMIjcOfPotu_p-YzdeC5KGKMru6WkWneLKgNM_vnZ3sDK36UrAadyDRBXYrGrRtEjmDRpAaUYVojvwKqay7XFdcTzXhzrSKlINi_LQAMpfnE_a7qzQDa-_UH3-0-tf/s400/spb8.png)
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjES56CpQ1theLii9LhsLQ9ECfMgJbQ-UiShs-9xFXqBSFJxd4yjkgMzKcRwcplLWlo0lKRyd4WEUNGqGatwRE71r1yFy_pTG9TubN_qiVm93KpmKoRpqXc_3d5srYXPfTayPoxUmm0s6Iy/s400/spb9.png)
上面談了這麼多
是不是要證明 SqlCommand.Parameters.Add 的第3個 size 參數沒有用呢?
當然不是,它當然有用,只是在上面範例中不適合
其實只要把字串改成全為英文,Parameter就會自動把超過的字串切掉
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBnZrP-9BeSW8-da4lhSdxM6dGsGNuCBUL9Ia6wD2e9pUNkN9SfCPL7joqo-8_6OjAXZUF9zi3-gZY_FMiUVHo6SMkYCkHftHelRbd6fGH4u6_e9VW-33zWm0zQn7PfMu1-dB63QoNMzat/s400/spb10.png)
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4s79RVGNiZ2wsUtUEJ-xv8Qfj3VrKOyo_otc2P806IcnqQyFlU-mEr6Tf6cBJNcpmXOEr2VE6L0A4_9JGu2RVJVbCxTbS2572wU0nFa7DnRBdeXcGBraSmtJZVmB1wbfxyewQVANRZRWW/s400/spb11.png)
上述例子中沒有作用的原因為:
中文字才6個,還未達到Length=10的瓶頸
但又因為中文字每個字代表2bytes,所以早就超過資料表裡的限制
字串切割會以字的數目,而非字所佔的容量
「一二3四五」和「12345」同樣是算 5 個字
用 String.Substring(0,6) 即會拋出 index 不符的例外
但其實上述的範例真正的解決方式為...
應將有可能出現中文字的欄位設為 nvarchar,且前端要針對輸入的字作控管
最後補充一下小小的發現
上面 Table 裡的 ID 值是用遞增方式,但卻少了 2,難道是坎尼自己偷刪嗎?
其實是上述的測試中,坎尼故意傳了大於欄位長度的值進去,因此在 Sql 方面出現例外
不過遞增值的 flag 似乎自動加一了? 所以才會跳過該數字
3 則留言:
用暴力法直接把超過長度的資料截斷,這感覺也不太對耶,我還以為超過 size 的話會丟出 exception 或者警告訊息?還是會留下 log?如果是默默把資料的尾巴去掉,那麼存到 datastore 中的資料就不完整了(資料遺失) ...
所以說要用 nvarchar 的格式才對
但那時主系統不是我們做的,我們沒有權力修改資料表,反應上去也一直沒回音
才只能在 AP 上動手腳,當然截斷之前會令使用者知道可能被截斷的訊息
剛剛再認真看了一下 MSDN (http://msdn.microsoft.com/en-us/library/40959t6x.aspx), 這說明還真是薄弱阿 ... 不過用 nvarchar 來存 unicode 的資料是最好的作法,至少以後要做資料轉檔的時候可以少處理一個編碼問題
張貼留言