參考鏈接:
- 在PG中存儲中文,現(xiàn)在大家的典型解決方法是用UTF8做數(shù)據(jù)庫編碼粘茄,但是用UTF8作數(shù)據(jù)庫編碼有一個問題签舞,就是中文排序的問題。
在PG中柒瓣,缺省的時候是按照編碼排序的儒搭,也就是按照UTF8的編碼對字段排序,但是芙贫,UTF8本身的編碼順序和人們習(xí)慣的中文的發(fā)音的拼音順序完全不同搂鲫,因而排序完全不是期望的拼音排序。那么如何解決呢磺平?
眾所周知魂仍,GBK編碼是按照拼音發(fā)音順序排序的,因此拣挪,解決方法之一就是把UTF8編碼轉(zhuǎn)換成GBK編碼擦酌。在這方面,PG提供了很好的支持函數(shù)菠劝。
PG提供的一些轉(zhuǎn)換函數(shù)
PG提供了很好的編碼轉(zhuǎn)換函數(shù):convert()赊舶,使用這個函數(shù),我們可以有效地把UTF8編碼轉(zhuǎn)換成GBK編碼赶诊。比如笼平,要把UTF8編碼轉(zhuǎn)換成GBK編碼:
select convert('UTF8編碼字串或字段' using utf8_to_gbk);
這樣就可以了,那么舔痪,是不是我們可以這樣實(shí)現(xiàn)GBK的讀音序編碼呢:
select * from table order by convert( column_need_to_sort_in_utf8 using utf8_to_gbk);
但是實(shí)際上試驗(yàn)的結(jié)果寓调,仍然不對,這是為什么呢锄码?
如何解決
原因在于捶牢,對于文本字段(text, varchar, char類型的字段),PG是使用底層OS的locale相關(guān)的函數(shù)進(jìn)行字符串比較的巍耗,眾所周知,排序的一個重要的事情就是需要字符串比較函數(shù)(幾乎所有的排序算法都涉及大于渐排、小于炬太、等于等過程)。而遺憾的是驯耻,因?yàn)楦鞣N原因(國家沒有投入是一個重要原因)亲族,在各種OS上的locale相關(guān)的函數(shù)集(比如 stroll)炒考,對漢字的排序比較都不是很標(biāo)準(zhǔn);也不是很正確霎迫。
怎么解決呢斋枢?從根本分析入手,就是要讓PG不使用OS的locale相關(guān)的東西知给,這樣瓤帚,解法之一是 initdb 的時候,使用C做locale涩赢,這個時候PG會用strcmp戈次,而不是stroll來比較字串大小。但是這樣也不一定很好筒扒,因?yàn)閟trcmp有時候在某些特殊的編碼的時候也會有些問題怯邪,并且,我們很多時候也需要locale花墩,比如在中文的全文索引的時候悬秉。
那么,有什么辦法讓PG一定用類似memcp(1)這樣的接口來比較數(shù)據(jù)么冰蘑?
答案當(dāng)然是有的:還記得PG有個數(shù)據(jù)類型是二進(jìn)制數(shù)據(jù)類型么和泌?它就是BYTEA,在PG中懂缕,所有的變長類型:TEXT允跑、BYTEA、 VARCHAR等的底層結(jié)構(gòu)都是一樣的搪柑,但是每個類型在SQL子句中調(diào)用的OS處理函數(shù)不同聋丝,比如BYTEA就是使用memcp(1)進(jìn)行排序的比較的,因此工碾,我們可以想辦法把TEXT的類型轉(zhuǎn)換成BYTEA進(jìn)行ORDER BY弱睦,這樣就可以即使用locale,又繞開stroll的限制渊额。
因?yàn)闆]有內(nèi)置的TEXT到BYTEA的轉(zhuǎn)換函數(shù)况木,我們需要自己做一個:
CREATE OR REPLACE FUNCTION text2bytea(text)
RETURNS bytea AS
$BODY$
begin
return $1;
end; $BODY$
LANGUAGE plpgsql immutable;
這個函數(shù)很簡單,利用了PG提供的text_in()和bytea_out的輸入輸出轉(zhuǎn)換函數(shù)旬迹,直接生成BYTEA的數(shù)據(jù)火惊。
于是,我們可以這樣進(jìn)行中文的語音排序了:
select * from table order by text2bytea(convert(column_need_to_sort_in_utf8 using utf8_to_gbk));