1 簡介 ORACLE資料庫字符集,即Oracle全球化支援(Globalization Support),或即國家語言支援(NLS)其作用是用本國語言和格式來儲存、處理和檢索資料。利用全球化支援,ORACLE為使用者提供自己熟悉的資料庫母語環境,諸如日期格式、數字格式和儲存序列等。Oracle可以支援多種語言及字符集,其中oracle8i支援48種語言、76個國家地域、229種字符集,而oracle9i則支援57種語言、88個國家地域、235種字符集。由於oracle字符集種類多,且在儲存、檢索、遷移Oracle資料時多個環節與字符集的設定密切相關,因此在實際的應用中,資料庫開發和管理人員經常會遇到有關oracle字符集方面的問題。本文通過以下幾個方面闡述,對oracle字符集做簡要分析 2 字符集基本知識 2.1字符集 實質就是按照一定的字元編碼方案,對一組特定的符號,分別賦予不同數值編碼的集合,是各個字元的一種二進位制表示(用位和位元組表示)。目前有多種不同的字符集,每種字符集能表示不同的字元。Oracle資料庫最早支援的編碼方案是US7ASCII。 Oracle的字符集命名遵循以下命名規則: 即: <語言><位元位數><編碼> 比如: ZHS16GBK表示採用GBK編碼格式、16位(兩個位元組)簡體中文字符集 2.2字元編碼方案 2.2.1單位元組編碼 (1)單位元組7位字符集,可以定義128個字元,最常用的字符集為US7ASCII (2)單位元組8位字符集,可以定義256個字元,適合於歐洲大部分國家 例如:WE8ISO8859P1(西歐、8位、ISO標準8859P1編碼) 2.2.2 多位元組編碼 (1)變長多位元組編碼 某些字元用一個位元組表示,其它字元用兩個或多個字元表示,變長多位元組編碼常用於對亞洲語言的支援, 例如日語、漢語、印地語等 例如:AL32UTF8(其中AL代表ALL,指適用於所有語言)、zhs16cgb231280 (2)定長多位元組編碼 每一個字元都使用固定長度位元組的編碼方案,目前oracle唯一支援的定長多位元組編碼是AF16UTF16,也是僅用於國家字符集 2.2.3 Unicode編碼 Unicode是一個涵蓋了目前全世界使用的所有已知字元的單一編碼方案,也就是說Unicode為每一個字元提供唯一的編碼。UTF-16是Unicode的16位編碼方式,是一種定長多位元組編碼,用2個位元組表示一個Unicode字元,AF16UTF16是UTF-16編碼字符集。 UTF-8是Unicode的8位編碼方式,是一種變長多位元組編碼,這種編碼可以用1、2、3個位元組表示一個Unicode字元,AL32UTF8,UTF8、UTFE是UTF-8編碼字符集 2.3 字符集超級 當一種字符集(字符集A)的編碼數值包含所有另一種字符集(字符集B)的編碼數值,並且兩種字符集相同編碼數值代表相同的字元時,則字符集A是字符集B的超級,或稱字符集B是字符集A的子集。 Oracle8i和oracle9i官方文件資料中備有子集-超級對照表(subset-superset pairs),例如:WE8ISO8859P1是WE8MSWIN1252的子集。由於US7ASCII是最早的Oracle資料庫編碼格式,因此有許多字符集是US7ASCII的超集,例如WE8ISO8859P1、ZHS16CGB231280、ZHS16GBK都是US7ASCII的超集。 2.4 資料庫字符集(oracle伺服器端字符集) 資料庫字符集在建立資料庫時指定,在建立後通常不能更改。在建立資料庫時,可以指定字符集(CHARACTER SET)和國家字符集(NATIONAL CHARACTER SET)。 2.4.1字符集 (1)用來儲存CHAR, VARCHAR2, CLOB, LONG等型別資料 (2)用來標示諸如表名、列名以及PL/SQL變數等 (3)用來儲存SQL和PL/SQL程式單元等 2.4.2國家字符集: (1)用以儲存NCHAR, NVARCHAR2, NCLOB等型別資料 (2)國家字符集實質上是為oracle選擇的附加字符集,主要作用是為了增強oracle的字元處理能力,因為NCHAR資料型別可以提供對亞洲使用定長多位元組編碼的支援,而資料庫字符集則不能。國家字符集在oracle9i中進行了重新定義,只能在Unicode編碼中的AF16UTF16和UTF8中選擇,預設值是AF16UTF16 2.4.3查詢字符集引數 可以查詢以下資料字典或檢視檢視字符集設定情況 nls_database_parameters、props$、v$nls_parameters 查詢結果中NLS_CHARACTERSET表示字符集,NLS_NCHAR_CHARACTERSET表示國家字符集 2.4.4修改資料庫字符集 按照上文所說,資料庫字符集在建立後原則上不能更改。如果需要修改字符集,通常需要匯出資料庫資料,重建資料庫,再匯入資料庫資料的方式來轉換,或通過ALTER DATABASE CHARACTER SET語句修改字符集,但建立資料庫後修改字符集是有限制的,只有新的字符集是當前字符集的超集時才能修改資料庫字符集,例如UTF8是US7ASCII的超集,修改資料庫字符集可使用ALTER DATABASE CHARACTER SET UTF8。 2.5 客戶端字符集(NLS_LANG引數) 2.5.1客戶端字符集含義 客戶端字符集定義了客戶端字元資料的編碼方式,任何發自或發往客戶端的字元資料均使用客戶端定義的字符集編碼,客戶端可以看作是能與資料庫直接連線的各種應用,例如sqlplus,exp/imp等。客戶端字符集是通過設定NLS_LANG引數來設定的。 2.5.2 NLS_LANG引數格式 NLS_LANG=_. Language:顯示oracle訊息,校驗,日期命名 Territory:指定預設日期、數字、貨幣等格式 Client character set:指定客戶端將使用的字符集 例如:NLS_LANG=AMERICAN_AMERICA.US7ASCII AMERICAN是語言,AMERICA是地區,US7ASCII是客戶端字符集 2.5.3客戶端字符集設定方法 1)UNIX環境 $NLS_LANG=“simplified chinese”_china.zhs16gbk $export NLS_LANG 編輯oracle使用者的profile檔案 2)Windows環境 編輯登錄檔 Regedit.exe---HKEY_LOCAL_MACHINE---SOFTWARE---ORACLE—HOME 2.5.4 NLS引數查詢 Oracle提供若干NLS引數定製資料庫和使用者機以適應本地格式,例如有NLS_LANGUAGE,NLS_DATE_FORMAT,NLS_CALENDER等,可以通過查詢以下資料字典或v$檢視檢視。 NLS_DATABASE_PARAMETERS--顯示資料庫當前NLS引數取值,包括資料庫字符集取值 NLS_SESSION_PARAMETERS--顯示由NLS_LANG 設定的引數,或經過alter session 改變後的引數值(不包括由NLS_LANG 設定的客戶端字符集) NLS_INSTANCE_PARAMETE--顯示由引數檔案init.ora 定義的引數V$NLS_PARAMETERS--顯示資料庫當前NLS引數取值 2.5.5修改NLS引數 使用下列方法可以修改NLS引數 (1)修改例項啟動時使用的初始化引數檔案 (2)修改環境變數NLS_LANG (3)使用ALTER SESSION語句,在oracle會話中修改 (4)使用某些SQL函式 NLS作用優先級別:Sql function>alter session>環境變數或登錄檔>引數檔案>資料庫預設引數 3 亂碼問題 Oracle在資料儲存、遷移過程中經常發生字元亂碼問題,歸根到底是由於字符集使用不當引起。下面以使用客戶端sqlplus向資料庫插入資料為例,說明亂碼產生的原因。 3.1使用客戶端sqlplus向資料庫儲存資料 這個過程存在3個字符集設定 (1)客戶端應用字符集 (2)客戶端NLS_LANG引數設定 (3)伺服器端資料庫字符集(Character Set)設定 客戶端應用Sqlplus中能夠顯示什麼樣的字元取決於客戶端作業系統語言環境(客戶端應用字符集),但在應用中錄入這些字元後,這些字元能否在資料庫中正常儲存,還與另外兩個字符集設定緊密相關,其中客戶端NLS_LANG引數主要用於字元資料傳輸過程中的轉換判斷。常見的亂碼大致有兩種情形: (1)漢字變成問號“?”; 當從字符集A 轉換成字符集B時,如果轉換字元之間不存在對應關係,NLS_LANG使用替代字元“?”替代無法對映的字元 (2)漢字變成未知字元(雖然有些是漢字,但與原字元含義不同) 轉換存在對應關係,但字符集A 中的字元編碼與字符集B 中的字元編碼代表不同含義 3.2發生亂碼原因 亂碼產生是由於幾個字符集之間轉換不匹配造成,分以下幾種情況: (注:字符集之間如果不存在子集、超集對應關係時的情況不予考慮,因為這種情況下字符集之間轉換必產生亂碼) 1)伺服器端資料庫字符集與客戶端應用字符集相同,與客戶端NLS_LANG引數設定不同 如果客戶端NLS_LANG字符集是其它兩種字符集的子集,轉換過程將出現亂碼。 解決方法:將三種字符集設定成同一字符集,或NLS_LANG字符集是其它兩種字符集的超集 2)伺服器端資料庫字符集與客戶端NLS_LANG引數設定相同,與客戶端應用字符集不同 如果客戶端應用字符集是其它兩種字符集的超集時,轉換過程將出現亂碼,但對於單位元組編碼儲存中文問題,可參看本文第5章節的分析 3)客戶端應用字符集、客戶端NLS_LANG引數設定、伺服器端資料庫字符集互不相同 此種情況較為複雜,但三種字符集之間只要有不能轉換的字元,則必產生亂碼 4 單位元組編碼儲存中文問題 由於歷史的原因,早期的oracle沒有中文字符集(如oracle6、oracle7、oracle7.1),但有的使用者從那時起就使用資料庫了,並用US7ASCII字符集儲存了中文,或是有的使用者在建立資料庫時,不考慮清楚,隨意選擇一個預設的字符集,如WE8ISO8859P1或US7ASCII,而這兩個字符集都沒有漢字編碼,雖然有些時候選用這種字符集好象也能正常使用,但用這種字符集儲存漢字資訊從原則上說就是錯誤的,它會給資料庫的使用與維護帶來一系列的麻煩。 正常情況下,要將漢字存入資料庫,資料庫字符集必須支援中文,而將資料庫字符集設定為US7ASCII等單位元組字符集是不合適的。US7ASCII字符集只定義了128個符號,並不支援漢字。另外,如果在SQL*PLUS中能夠輸入中文,作業系統預設應該是支援中文的,但如果在NLS_LANG中的字符集設定為US7ASCII,顯然也是不正確的,它沒有反映客戶端的實際情況。但在實際應用中漢字顯示卻是正確的,這主要是因為Oracle檢查資料庫與客戶端的字符集設定是同樣的,那麼資料在客戶與資料庫之間的存取過程中將不發生任何轉換,但是這實際上導致了資料庫標識的字符集與實際存入的內容是不相符的。而在SELECT的過程中,Oracle同樣檢查發現數據庫與客戶端的字符集設定是相同的,所以它也將存入的內容原封不動地傳送到客戶端,而客戶端作業系統識別出這是漢字編碼所以能夠正確顯示。 在這個例子中,資料庫與客戶端都沒有設定成中文字符集,但卻能正常顯示中文,從應用的角度看好象沒問題。然而這裡面卻存在著極大的隱患,比如在應用length或substr等字串函式時,就可能得到意外的結果。 對於早期使用US7ASCII字符集資料庫的資料遷移到oracle8i/9i中(使用zhs16gbk),由於原始資料已經按照US7ASCII格式儲存,對於這種情況,可以通過使用Oracle8i的匯出工具,設定匯出字符集為US7ASCII,匯出後使用UltraEdit等工具開啟dmp檔案,修改第二、三字元,修改 0001 為0354,這樣就可以將US7ASCII字符集的資料正確匯入到ZHS16GBK的資料庫中。 為了避免在資料庫遷移過程中由於字符集不同導致的資料損失,oracle提供了字符集掃描工具(character set scanner),通過這個工具我們可以測試在資料遷移過程中由於字符集轉換可能帶來的問題,然後根據測試結果,確定資料遷移過程中最佳字符集解決方案。 5 Oracle資料庫字符集問題解析 5.1字符集的概念。 我們知道,電子計算機最初是用來進行科學計算的(所以叫做“計算機”),但隨著技術的發展,還需要計算機進行其它方面的應用處理。這就要求計算機不僅能處理數值,還能處理諸如文字、特殊符號等其它資訊,而計算機本身能直接處理的只有數值資訊,所以就要求對這些文字、符號資訊進行數值編碼,最初的字符集是我們都非常熟悉的ASCII,它是用7個二進位制位來表示128個字元,而後來隨著不同國家、組織的需要,出現了許許多多的字符集,如表示西歐字元的ISO8859系列的字符集,表示漢字的GB2312-80、GBK等字符集。 字符集的實質就是對一組特定的符號,分別賦予不同的數值編碼,以便於計算機的處理。 5.2字符集之間的轉換。 字符集多了,就會帶來一個問題,比如一個字元,在某一字符集中被編碼為一個數值,而在另一個字符集中被編碼為另一個數值,比如我來創造兩個字符集demo_charset1與demo_charset2,在demo_charset1中,我規定了三個符號的編碼為:A(0001),B(0010),?(1111);而在demo_charset2中,我也規定了三個符號的編碼為:A(1001),C(1011),?(1111),這時我接到一個任務,要編寫一個程式,負責在demo_charset1與demo_charset2之間進行轉換。由於知道兩個字符集的編碼規則,對於demo_charset1中的0001,在轉換為demo_charset2時,要將其編碼改為1001;對於demo_charset1中的1111,轉換為demo_charset2時,其數值不變;而對於demo_charset1中的0010,其對應的字元為B,但在demo_charset2沒有對應的字元,所以從理論上無法轉換,對於所有這類無法轉換的情況,我們可以將它們統一轉換為目標字符集中的一個特殊字元(稱為“替換字元”),比如在這裡我們可以將?作為替換字元,所以B就轉換為了?,出現了資訊的丟失;同樣道理,將demo_charset2的C字元轉換到demo_charset1時,也會出現資訊丟失。 所以說,在字符集轉換過程中,如果源字符集中的某個字元在目標字符集中沒有定義,將會出現資訊丟失。 5.3資料庫字符集的選擇 我們在建立資料庫時,需要考慮的一個問題就是選擇什麼字符集與國家字符集(通過create database中的CHARACTER SET與NATIONAL CHARACTER SET子句指定)。考慮這個問題,我們必須要清楚資料庫中都需要儲存什麼資料,如果只需要儲存英文資訊,那麼選擇US7ASCII作為字符集就可以;但是如果要儲存中文,那麼我們就需要選擇能夠支援中文的字符集(如ZHS16GBK);如果需要儲存多國語言文字,那就要選擇UTF8了。 資料庫字符集的確定,實際上說明這個資料庫所能處理的字元的集合及其編碼方式,由於字符集選定後再進行更改會有諸多的限制,所以在資料庫建立時一定要考慮清楚後再選擇。而我們許多朋友在建立資料庫時,不考慮清楚,往往選擇一個預設的字符集,如WE8ISO8859P1或US7ASCII,而這兩個字符集都沒有漢字編碼,所以用這種字符集儲存漢字資訊從原則上說就是錯誤的。雖然在有些時候選用這種字符集好象也能正常使用,但它會給資料庫的使用與維護帶來一系列的麻煩,在後面的迭代過程中我們將深入分析。 5.4客戶端的字符集 有過一些Oracle使用經驗的朋友,大多會知道通過NLS_LANG來設定客戶端的情況,NLS_LANG由以下部分組成:NLS_LANG=_.,其中第三部分的本意就是用來指明客戶端作業系統預設使用的字符集。所以按正規的用法,NLS_LANG應該按照客戶端機器的實際情況進行配置,尤其對於字符集一項更是如此,這樣Oracle就能夠在最大程度上實現資料庫字符集與客戶端字符集的自動轉換(當然是如果需要轉換的話)。 5.5通過例項加深對基本概念的理解 下面我將引用網友tellin在ITPUB上發表的“CHARACTER SET研究及疑問”帖子,該朋友在帖子中列舉了他做的相關實驗,並對實驗結果提出了一些疑問,我將對他的實驗結果進行分析,並回答他的疑問。 5.5.1實驗結果分析一 最初由 tellin 釋出 設定客戶端字符集為US7ASCII D:\>SET NLS_LANG=AMERICAN_AMERICA.US7ASCII 檢視伺服器字符集為US7ASCII SQL> SELECT * FROM NLS_DATABASE_PARAMETERS; PARAMETER VALUE ------------------------------ ---------------------------------------- NLS_CHARACTERSET US7ASCII 建立測試表 SQL> CREATE TABLE TEST (R1 VARCHAR2(10)); Table created. 插入資料 SQL> INSERT INTO TEST VALUES('東北'); 1 row created. SQL> SELECT * FROM TEST; R1 ---------- 東北 SQL> EXIT 這一部分的實驗資料的存取與顯示都正確,好象沒什麼問題,但實際上卻隱藏著很大的隱患。 首先,要將漢字存入資料庫,而將資料庫字符集設定為US7ASCII是不合適的。US7ASCII字符集只定義了128個符號,並不支援漢字。另外,由於在SQL*PLUS中能夠輸入中文,作業系統預設應該是支援中文的,但在NLS_LANG中的字符集設定為US7ASCII,顯然也是不正確的,它沒有反映客戶端的實際情況。 但實際顯示卻是正確的,這主要是因為Oracle檢查資料庫與客戶端的字符集設定是同樣的,那麼資料在客戶與資料庫之間的存取過程中將不發生任何轉換。具體地說,在客戶端輸入“東北”,“東”的漢字的編碼為182(10110110)、171(10101011),“北”漢字的編碼為177(10110001)、177(10110001),它們將不做任何變化的存入資料庫中,但是這實際上導致了資料庫標識的字符集與實際存入的內容是不相符的,從某種意義上講,這也是一種不一致性,也是一種錯誤。而在SELECT的過程中,Oracle同樣檢查發現數據庫與客戶端的字符集設定是相同的,所以它也將存入的內容原封不動地傳送到客戶端,而客戶端作業系統識別出這是漢字編碼所以能夠正確顯示。 在這個例子中,資料庫與客戶端的設定都有問題,但卻好象起到了“負負得正”的效果,從應用的角度看倒好象沒問題。但這裡面卻存在著極大的隱患,比如在應用length或substr等字串函式時,就可能得到意外的結果。另外,如果遇到匯入/匯出(import /export)將會遇到更大的麻煩。有些朋友在這方面做了大量的測試,如eygle研究了“源資料庫字符集為US7ASCII,匯出檔案字符集為US7ASCII或ZHS16GBK,目標資料庫字符集為ZHS16GBK”的情況,他得出的結論是 “如果的是在Oracle92中,我們發現對於這種情況,不論怎樣處理,這個匯出檔案都無法正確匯入到Oracle9i資料庫中”、“對於這種情況,我們可以通過使用Oracle8i的匯出工具,設定匯出字符集為US7ASCII,匯出後修改第二、三字元,修改 0001 為0354,這樣就可以將US7ASCII字符集的資料正確匯入到ZHS16GBK的資料庫中”。我想對於這些結論,這樣理解可能更合適一些:由於ZHS16GBK字符集是US7ASCII的超級,所以如果按正常操作,這種轉換應該沒有問題;但出現問題的本質是我們讓本應只儲存英文字元的US7ASCII資料庫,非常規地儲存了中文資訊,那麼在轉化過程中出現錯誤或麻煩就沒什麼奇怪的了,不出麻煩倒是有些奇怪了。 所以說要避免這種情況,就是要在建立資料庫時選擇合適的字符集,不讓標籤(資料庫的字符集設定)與實際(資料庫中實際儲存的資訊)不符的情況發生 5.5.2實驗結果分析二 [ 更改客戶端字符集為ZHS16GBK D:\>SET NLS_LANG=AMERICAN_AMERICA.ZHS16GBK D:\>SQLPLUS "/ AS SYSDBA" 無法正常顯示資料 SQL> SELECT * FROM TEST; R1 -------------------- 6+11 疑問1:ZHS16GBK為US7ASCII的超集,為什麼在ZHS16GBK環境下無法正常顯示 這主要是因為Oracle檢查發現數據庫設定的字符集與客戶端配置字符集不同,它將對資料進行字符集的轉換。資料庫中實際存放的資料為182(10110110)、171(10101011)、177(10110001)、177(10110001),由於資料庫字符集設定為US7ASCII,它是一個7bit的字符集,儲存在8bit的位元組中,則Oracle忽略各位元組的最高bit,則182(10110110)就變成了54(0110110),在ZHS16GBK中代表數字符號“6”(當然在其它字符集中也是“6”),同樣過程也發生在其它3個位元組,這樣“東北”就變成了“6+11”。 5.5.3實驗結果分析三 最初由 tellin 釋出 用ZHS16GBK插入資料 SQL> INSERT INTO TEST VALUES('東北'); 1 row created. SQL> SELECT * FROM TEST; R1 -------------------- 6+11 ?? SQL> EXIT 當客戶端字符集設定為ZHS16GBK後向資料庫插入“東北”,Oracle檢查發現數據庫設定的字符集為US7ASCII與客戶端不一致,需要進行轉換,但字符集ZHS16GBK中的“東北”兩字在US7ASCII中沒有對應的字元,所以Oracle用統一的“替換字元”插入資料庫,在這裡為“?”,編碼為63(00111111),這時,輸入的資訊實際上已經丟失,不管字符集設定如何改變(如下面引用的實驗結果),第二行SELECT出來的結果也都是兩個“?”號(注意是2個,而不是4個)。 更改客戶端字符集為US7ASCII D:\>SET NLS_LANG=AMERICAN_AMERICA.US7ASCII D:\>SQLPLUS "/ AS SYSDBA" 無法顯示用ZHS16GBK插入的字符集,但可以顯示用US7ASCII插入的字符集 SQL> SELECT * FROM TEST; R1 ---------- 東北 ?? 更改伺服器字符集為ZHS16GBK SQL> update props$ set value$='ZHS16GBK' WHERE NAME='NLS_CHARACTERSET'; 1 row updated. SQL> COMMIT; 更改客戶端字符集為ZHS16GBK D:\>SET NLS_LANG=AMERICAN_AMERICA.ZHS16GBK D:\>SQLPLUS "/ AS SYSDBA" 可以顯示以前US7ASCII的字符集,但無法顯示用ZHS16GBK插入的資料,說明用ZHS16GBK插入的資料為亂碼。 SQL> SELECT * FROM TEST; R1 -------------------- 東北 ?? 需要指出的是,通過“update props$ set value$='ZHS16GBK' WHERE NAME='NLS_CHARACTERSET';”來修改資料庫字符集是非常規作法,很可能引起問題,在這裡只是原文引用網友的實驗結果。 5.5.4實驗結果分析四 SQL> INSERT INTO TEST VALUES('東北'); 1 row created. SQL> SELECT * FROM TEST; R1 -------------------- 東北 ?? 東北 SQL> EXIT 由於此時資料庫與客戶端的字符集設定均為ZHS16GBK,所以不會發生字符集的轉換,第一行與第三行資料顯示正確,而第二行由於儲存的資料就是63(00111111),所以顯示的是“?”號。 更改客戶端字符集為US7ASCII D:\>SET NLS_LANG=AMERICAN_AMERICA.US7ASCII D:\>SQLPLUS "/ AS SYSDBA" 無法顯示資料 SQL> SELECT * FROM TEST; R1 ---------- ?? ?? ?? 疑問2:第一行資料是用US7ASCII環境插入的,為何無法正常顯示? 將客戶端字符集設定改為US7ASCII後進行SELECT,Oracle檢查發現數據庫設定的字符集為ZHS16GBK,資料需要進行字符集轉換,而第一行與第三行的漢字“東”與“北”在客戶端字符集US7ASCII中沒有對應字元,所以轉換為“替換字元”(“?”),而第二行資料在資料庫中存的本來就是兩個“?”號,所以雖然在客戶端顯示的三行都是兩個“?”號,但在資料庫中儲存的內容卻是不同的。 5.5.5實驗結果分析五 SQL> INSERT INTO TEST VALUES('東北'); 1 row created. SQL> EXIT 更改客戶端字符集為ZHS16GBK D:\>SET NLS_LANG=AMERICAN_AMERICA.ZHS16GBK D:\>SQLPLUS "/ AS SYSDBA" 無法顯示用US7ASCII插入的字符集,但可以顯示用ZHS16GBK插入的字符集[/COLOR] SQL> SELECT * FROM TEST; R1 -------------------- 東北 ?? 東北 6+11 SQL> 疑問3:US7ASCII為ZHS16GBK的子集,為何在US7ASCII環境下插入的資料無法顯示? 在客戶端字符集設定為US7ASCII時,向字符集為ZHS16GBK的資料庫中插入“東北”,需要進行字元轉換,“東北”的ZHS16GBK編碼為182(10110110)、171(10101011)與177(10110001)、 177(10110001),由於US7ASCII為7bit編碼,Oracle將這兩個漢字當作四個字元,並忽略各位元組的最高位,從而存入資料庫的編碼就變成了54(00110110)、43(00101011)與49(00110001)、49(00110001),也就是“6+11”,原始資訊被改變了。這時,將客戶端字符集設定為ZHS16GBK再進行SELECT,資料庫中的資訊不需要改變傳到客戶端,第一、三行由於存入的資訊沒有改變能顯示“東北”,而第二、四行由於插入資料時資訊改變,所以不能顯示原有資訊了。 6 兩條原則 分析了這麼多的內容,但實際上總結起來也很簡單,要想在字符集方面少些錯誤與麻煩,需要堅持兩條基本原則: 在資料庫端:選擇需要的字符集(通過Create Database中的CHARACTER SET與NATIONAL CHARACTER SET子句指定); 在客戶端:設定作業系統實際使用的字符集(通過環境變數NLS_LANG設定)。 7 參考 1 《理解ORACLE資料庫字符集》 轉載於網路,來源不明,內容有刪減,謹致感謝。 2 《Oracle資料庫字符集問題解析》轉載於ITPUB論壇,謹致感謝