(轉)oracle字符集與漢字

Oracle與漢字問題與字符集

分類: oracle 425人閱讀 評論(0) 收藏 舉報

      Oracle字符集引發的幾個問題,常見的就是漢字佔多少個字節,其次就是字符集致使數據庫啓動失敗以及索引失效等問題html

漢字佔多少個字節?sql

select length('ABCDE中文字符串FG'),lengthb('ABCDE中文字符串FG') from dual;數據庫

就能夠知道,一個漢字佔了幾個字節,也能夠查看數據庫的字符集session

select * from nls_database_parameters where parameter ='NLS_CHARACTERSET';oracle

當NLS_CHARACTERSET=AL32UTF8時(UTF-8是變長編碼,每一個Unicode代碼點按照不一樣範圍,能夠有1-3字節的不一樣長度
NLS_LENGTH_SEMANTICS=BYTE時,一個漢字表明三個字節
NLS_LENGTH_SEMANTICS=CHAR時,一個漢字表明一個字節
當NLS_CHARACTERSET=US7ASCII時(字符集爲單字節)
NLS_LENGTH_SEMANTICS=BYTE時,一個漢字表明兩個字節
NLS_LENGTH_SEMANTICS=CHAR時,一個漢字表明兩個字節函數

Oracle與漢字問題相關的函數工具

注意:計算長度的幾個方法區別以下:post

LENGTH(string1) 返回以字符爲單位的長度.
LENGTHB(string1) 返回以字節爲單位的長度.
LENGTHC(string1) 返回以Unicode徹底字符爲單位的長度.
LENGTH2(string1) 返回以UCS2代碼點爲單位的長度.
LENGTH4(string1) 返回以UCS4代碼點爲單位的長度.測試

substr,substrb均爲字符串截取函數,都帶有三個參數,第一個參數爲所要截取的字符串,第二個參數爲strart(索引均從1開始),第三個參數爲length。
substr是按照字來算的,而substrb()是按照字節來算編碼

關於substr,substrb的例子舉例:

SQL> select substr('今天是個好日子',3,5) from dual;
----------
是個好日子
SQL> select substrb('今天是個好日子',3,5) from dual;
-----
天是
結論是substr是按照字來算的,而substrb()是按照字節來算的。看下面的例子:
SQL> select substr('abcdef',3,4) from dual;
----
cdef
SQL> select substrb('abcdef',3,4) from dual;
----
cdef
分析:對於字母來講,substr與substrb做用時同樣的,但對於漢字來講,substr是按字來取值,而substrb是按字節來取值,當所取長度爲奇數時,則自動捨棄最後一位字節。
相似的還有,
length與lengthb 長度計算函數 
select length('你好') from dual         ----output:2
select lengthb('你好') from dual       ----output :4

Instr與Instrb 字符串查找函數 instr(原字符串,查的字符串,起始位置,第幾個匹配) 返回字符串位置,找不到返回0 .
select instr('日日花前長病酒','花前',1,1) from dual     ----output:3
select instrb('日日花前長病酒','花前',1,1) from dual     ----output:7

 Oracle字符集

安裝數據庫的時候能夠設置字符集,不一樣版本可能默認的字符集是不同的(以Oracle 9i爲例子)

首先查看字符集:(注意:修改數據庫字符集時必須謹慎,修改以前必定要爲數據庫備份。因爲不能回退這項操做,所以可能會形成數據丟失或者損壞)

SQL> select name,value$ from props$ where name like '%NLS%';

NAME                           VALUE$
------------------------------ ------------------------------
NLS_LANGUAGE                   AMERICAN
NLS_TERRITORY                  AMERICA
NLS_CURRENCY                   $
NLS_ISO_CURRENCY               AMERICA
NLS_NUMERIC_CHARACTERS         .,
NLS_CHARACTERSET               US7ASCII
NLS_CALENDAR                   GREGORIAN
NLS_DATE_FORMAT                DD-MON-RR
NLS_DATE_LANGUAGE              AMERICAN
……………….
NLS_NCHAR_CHARACTERSET         AL16UTF16
NLS_RDBMS_VERSION              9.2.0.4.0

20 rows selected.
SQL> select name,dump(name) from eygle.test;

NAME   DUMP(NAME)
------------------------------------------------------
測試     Typ=1 Len=4: 178,226,202,212
Test      Typ=1 Len=4: 116,101,115,116


2 rows selected.

轉換字符集,你只能在新字符集是舊字符集嚴格超集的狀況下使用這種方式轉換。所謂超集是指:當前字符集中的每個字符在新字符集中均可以表示,並使用一樣的代碼點好比不少字符集都是US7ASCII的嚴格超集.

若是不是超集,將得到如下錯誤:

  1. SQL> ALTER DATABASE CHARACTER SET  ZHS16CGB231280;  
  2. ALTER DATABASE CHARACTER SET  ZHS16CGB231280*ERROR at line 1:ORA-12712: new character set must be a superset of old character set  

轉換字符集,數據庫應該在RESTRICTED模式下進行:

  1. <p>c:\>sqlplus "/ as sysdba"  
  2.   
  3. SQL*Plus: Release 9.2.0.4.0 - Production on Sat Nov 1 10:52:30 2003  
  4.   
  5. Copyright (c) 1982, 2002, Oracle Corporation.  All rights reserved.  
  6.   
  7.   
  8. Connected to:  
  9. Oracle9i Enterprise Edition Release 9.2.0.4.0 - Production  
  10. With the Partitioning, Oracle Label Security, OLAP and Oracle Data Mining options  
  11. JServer Release 9.2.0.4.0 - Production  
  12.   
  13. SQL> shutdown immediate  
  14. Database closed.  
  15. Database dismounted.  
  16. ORACLE instance shut down.  
  17. SQL> STARTUP MOUNT;  
  18. ORACLE instance started.  
  19.   
  20. Total System Global Area   76619308 bytes  
  21. Fixed Size                   454188 bytes  
  22. Variable Size              58720256 bytes  
  23. Database Buffers           16777216 bytes  
  24. Redo Buffers                 667648 bytes  
  25. Database mounted.  
  26. SQL> ALTER SESSION SET SQL_TRACE=TRUE;  
  27.   
  28. Session altered.  
  29.   
  30. SQL> ALTER SYSTEM ENABLE RESTRICTED SESSION;  
  31.   
  32. System altered.  
  33.   
  34. SQL> ALTER SYSTEM SET JOB_QUEUE_PROCESSES=0;  
  35.   
  36. System altered.  
  37.   
  38. SQL> ALTER SYSTEM SET AQ_TM_PROCESSES=0;  
  39.   
  40. System altered.  
  41.   
  42. SQL> ALTER DATABASE OPEN;  
  43.   
  44. Database altered.  
  45.   
  46. SQL> set linesize 120  
  47. SQL> ALTER DATABASE CHARACTER SET ZHS16GBK;  
  48. ALTER DATABASE CHARACTER SET ZHS16GBK  
  49. *  
  50. ERROR at line 1:  
  51. ORA-12721: operation cannot execute when other sessions are active  
  52.   
  53.   
  54. SQL> ALTER DATABASE CHARACTER SET ZHS16GBK;  
  55. ALTER DATABASE CHARACTER SET ZHS16GBK  
  56. *  
  57. ERROR at line 1:  
  58. ORA-12716: Cannot ALTER DATABASE CHARACTER SET when CLOB data exists  
  59.   
  60. 在Oracle9i中,若是數據庫存在CLOB類型字段,那麼就不容許對字符集進行轉換  
  61. </p>  

這時候,咱們能夠去查看alert<sid>.log日誌文件,看CLOB字段存在於哪些表上:

  1. ALTER DATABASE CHARACTER SET ZHS16GBK SYS.METASTYLESHEET (STYLESHEET) - CLOB populatedORA-12716 signalled during: ALTER DATABASE CHARACTER SET ZHS16GBK...   

對於不一樣狀況,Oracle提供不一樣的解決方案,若是是用戶數據表,通常咱們能夠把包含CLOB字段的表導出,而後drop掉相關對象,
轉換後再導入數據庫;對於系統表,能夠按照如下方式處理:

[html] view plain copy
  1. SQL> truncate table Metastylesheet;Table truncated.   
  2. SQL> ALTER SESSION SET SQL_TRACE=TRUE;  
  3. Session altered.  
  4. SQL> ALTER DATABASE CHARACTER SET ZHS16GBK;  
  5. Database altered.  
  6. SQL> ALTER SESSION SET SQL_TRACE=FALSE;  
  7. Session altered.  

在9.2.0中,轉換完成之後,能夠經過運行catmet.sql腳原本重建Metastylesheet表:

SQL> @?/rdbms/admin/catmet.sql

經過Metastylesheet表來測試不一樣字符集的影響。

提示:
經過設置sql_trace,咱們能夠跟蹤不少數據庫的後臺操做,這個工具是DBA經常使用的「利器」之一。
咱們簡單看一下數據庫更改字符集時的後臺處理,我提取了主要的更新部分。
經過如下跟蹤過程,咱們看到數據庫在更改字符集的時候,主要更新了12張數據字典表,修改了數據庫的原數據,這也證明了咱們之前的說法:
這個更改字符集的操做在本質上並不轉換任何數據庫字符,只是簡單的更新數據庫中全部跟字符集相關的信息。

  1. update col$ set charsetid = :1   
  2. where  
  3.  charsetform = :2  
  4.   
  5.   
  6. update argument$ set charsetid = :1   
  7. where  
  8.  charsetform = :2  
  9.   
  10.   
  11. update collection$ set charsetid = :1   
  12. where  
  13.  charsetform = :2  
  14.   
  15.   
  16. update attribute$ set charsetid = :1   
  17. where  
  18.  charsetform = :2  
  19.   
  20.   
  21. update parameter$ set charsetid = :1   
  22. where  
  23.  charsetform = :2  
  24.   
  25.   
  26. update result$ set charsetid = :1   
  27. where  
  28.  charsetform = :2  
  29.   
  30.   
  31. update partcol$ set spare1 = :1   
  32. where  
  33.  charsetform = :2  
  34.   
  35.   
  36. update subpartcol$ set spare1 = :1   
  37. where  
  38.  charsetform = :2  
  39.   
  40.   
  41. update props$ set value$ = :1   
  42. where  
  43.  name = :2  
  44.   
  45.   
  46. update "SYS"."KOTAD$" set SYS_NC_ROWINFO$ = :1   
  47. where  
  48.  SYS_NC_OID$ = :2  
  49.   
  50. update seq$ set increment$=:2,minvalue=:3,maxvalue=:4,cycle#=:5,order$=:6,  
  51.   cache=:7,highwater=:8,audit$=:9,flags=:10   
  52. where  
  53.  obj#=:1  
  54.   
  55. update kopm$ set metadata = :1, length  = :2   
  56. where  
  57.  name='DB_FDO'  

注意:經過前面 」 ALTER DATABASE CHARACTER SET」 方式更改字符集時,Oracle至少須要更改12張數據字典表,而這種直接更新props$表的方式只完成了其中十二分之一的工做,潛在的完整性隱患是可 想而知的。並且經過更新props$表的方式修改字符集,在Oracle7以後就不該該被使用.

相關文章
相關標籤/搜索