說明:1、char:固定長度的非 Unicode 字符數(shù)據(jù),最大長度為 8,000 個字符。
2、varchar:可變長度的非 Unicode 數(shù)據(jù),最長為 8,000 個字符。
3、nvarchar:可變長度 Unicode 數(shù)據(jù),其最大長度為 4,000 字符。
4、nchar:固定長度的 Unicode 數(shù)據(jù),最大長度為 4,000 個字符。
5、char和varchar都是字符串類型的,用Unicode編碼的字符串,結(jié)果是字符的整數(shù)值.
如有以下數(shù)據(jù)結(jié)構(gòu):
- 工號 姓名 部門
- ———————–
- 1 張三 財務(wù)
- 2 李四 人事
- 3 王五 銷售
- ……..
我們定義”姓名”為char(10)(靜態(tài))的時簡單地用php代碼表示,簡單地模擬底層數(shù)據(jù)存儲鏈表$data,代碼如下:
- $col_num_len =1; //工號長度為1
- $col_name_len=10; //姓名長度為10
- $col_unit_len =4; //部門長度為4
- $col_len=$col_num_len+$col_name_len+$col_unit_len+3;
- //表示每筆記錄的總長度,包括3個分隔符
實現(xiàn)如下,代碼如下:
- $data="1|張三 |財務(wù)|2|李四 |人事|3|王五 |銷售|..."; //簡單地模擬底層數(shù)據(jù)存儲鏈表
- //假設(shè)查找第2條記錄的"姓名"字段數(shù)據(jù)
- $record_start=$col_len*1+1; //獲取第2行的起始位置
- $record =substr($data,$record_start,$col_len); //獲取第2條記錄
- $col_name_start=$col_num_len+2; //獲取"姓名"字段的起始位置
- $col_name=substr($record,$col_name_start,$col_name_len);//獲取"姓名"字段的數(shù)據(jù)
- echo $col_name;
代碼如下:
- //假設(shè)更新第2條記錄的"姓名"字段數(shù)據(jù)為"李小四"
- $update_info="李小四";
- $data=substr_replace($data,$update_info,$col_name_start,$col_name_len); //更新字段,流程結(jié)束
而如果我們定義”姓名”字段為varchar(10)(動態(tài))的時候情況則要復(fù)雜,注意存儲”姓名”的字段沒有空格,這是char和varchar的存儲區(qū)別:
- $col_num_len =1; //工號長度為1
- $col_name_len=10; //姓名長度為10
- $col_unit_len =4; //部門長度為4
- $col_len=$col_num_len+$col_name_len+$col_unit_len+3;
實現(xiàn)如下,代碼如下:
- //動態(tài)存放數(shù)據(jù)行的起始位置,數(shù)據(jù)為更新時生成(重新)
- $record_1_start=1;$record_1_name_dynamic_len=4; //$col_1_name_dynamic_len記錄"姓名"動態(tài)字段的長度
- $record_2_start=13;$record_2_name_dynamic_len=4;
- $record_3_start=26;$record_3_name_dynamic_len=6;
- ...
- $data="1|張三|財務(wù)|2|李四|人事|3|王小明|銷售|..."; //簡單地模擬底層數(shù)據(jù)存儲鏈表,注意存儲"姓名"的字段沒有空格
- //假設(shè)查找第2條記錄的"姓名"字段數(shù)據(jù)
- $record_2_end=$record_3_start-1; //獲取第2行的結(jié)束位置
- $record =substr($data,$record_2_start,$record_2_end); //獲取第2條記錄
- $col_name_start=$col_num_len+2; //獲取"姓名"字段的起始位置
- $col_name=substr($record,$col_name_start,$record_2_name_dynamic_len); //獲取"姓名"字段的數(shù)據(jù)
- echo $col_name;
- //假設(shè)更新第2條記錄的"姓名"字段數(shù)據(jù)為"李小四",這邊比靜態(tài)的復(fù)雜很多
- $update_info="李小四";
- $update_len=strlen($update_info); //獲取更新內(nèi)容的長度
- if($diff_len=$update_len-$record_2_name_dynamic_len)
- {
- $data=substr_replace($data,"",$col_name_start,$record_2_name_dynamic_len); //清除原先數(shù)據(jù)
- $record_2_name_dynamic_len=$update_len; //更新字段的長度(并存儲新值)
- //在此假設(shè)總記錄數(shù)為n
- for($i=2;$i<=n;$i++)
- { //Vevb.com
- ${'record_'.$i.'_start'}=${'record_'.$i.'_start'}+$diff_len; //重新更新每個行的起始位置(并存儲新值),系統(tǒng)開銷大(實際上有不同的方法解決)
- }
- }
- $data=substr_replace($data,$update_info,$col_name_start,0);
文中直接使用”substr_replace”,而在數(shù)據(jù)量很大的時候,底層實現(xiàn)上的開銷也是不小的,在mysql中表現(xiàn)為(Row Migration)現(xiàn)象,在此不作贅述.
根據(jù)以上的粗略實現(xiàn)證明:
1、varchar類型在更新環(huán)節(jié)上的系統(tǒng)開銷是遠(yuǎn)大于char類型的。
2、兩者間查找搜索性能上是不相上下的。
3、兩者間的存儲數(shù)據(jù)量($data)環(huán)節(jié)上,char要顯示大于varchar。
4、大數(shù)據(jù)量提取時varchar的磁盤IO消耗更低,意味著varchar綜合查詢性能會更好。
5、沒有了。
實際應(yīng)用中的結(jié)論(如在mysql中):
1、char適合字段頻繁更新時的應(yīng)用。
2、varchar更節(jié)省磁盤空間。
3、實際應(yīng)用中大數(shù)據(jù)量(多行)查詢返回,varchar的查詢性能比起char來要好出不少。
4、選擇char和varchar會改變整體數(shù)據(jù)結(jié)構(gòu)的算法以及存儲方式。在mysql應(yīng)用中,如已存在varchar字段,那么其它所有的char字段將以varchar方式存儲。
5、沒有了。
(以上算法僅以PHP簡單描述,歡迎更好的思路加以指教)
注:此文原作者的寫作時間比較久遠(yuǎn)了,所以有些地方和現(xiàn)在的有些出入,體現(xiàn)在:
1.在innodb引擎中,char和varchar的實現(xiàn)已無異,效率上并沒多大區(qū)別。
2.選擇char和varchar并不會改變整體數(shù)據(jù)結(jié)構(gòu)的算法以及存儲方式,我記得這是在MYSQL4里的特性,網(wǎng)上的老文章有講述,到了MYSQL5實測已無此特性.
總結(jié)分析:
文字字段若長度固定,如:身分證號碼,就不要用 varchar 或 nvarchar,應(yīng)該用 char 或 nchar.
支持多語言的站點應(yīng)考慮使用 Unicode nchar 或 nvarchar 數(shù)據(jù)類型以盡量減少字符轉(zhuǎn)換問題.
文字字段若長度不固定,如:地址,則該用 varchar 或 nvarchar。除了可節(jié)省存儲空間外,存取硬盤時也會較有效率.
新聞熱點
疑難解答
圖片精選