oracle字符编码的理解

时间:2023-01-10 22:32:01

原文链接:http://blog.csdn.net/dbanote/article/details/9158367

一、查询服务端字符集
    select userenv('language') from dual; 

    USERENV('LANGUAGE')
    ----------------------------------------------------
    SIMPLIFIED CHINESE_CHINA.ZHS16GBK


二、 客户端NLS_LANG参数(即sqlplus的参数)该参数用于向Oracle指示客户端操作系统(sqlplus客户端)的字符集。
    select * from v$nls_parameters where parameter='NLS_CHARACTERSET';  

    PARAMETER                      VALUE
    ------------------------------ ------------------------------
    NLS_CHARACTERSET               ZHS16GBK


    设置客户端NLS_LANG 
    C:\Users\85243>set nls_lang=Simplified Chinese_China.ZHS16GBK
    C:\Users\85243>set nls_lang=Simplified Chinese_China.AL32UTF8

三、客户端操作系统字符集
    C:\Users\85243>chcp
    活动代码页: 936
    936代表的是gbk编码 65001代表的是utf-8
  gbk编码包括所有中文,所有字符两个字节,
  utf-8是包括所有文字,英文字符为一个字节,其余的是三个字节

 

有了以上3个基本概念之后,我来阐述一下Oracle字符集转换的基本原则:
  1.设置客户端的NLS_LANG为客户端操作系统的字符集
  2.如果数据库字符集等于NLS_LANG,数据库和客户端传输字符时不作任何转换
  3.如果它们俩不等,则需要在不同字符集间转换,只有客户端操作系统字符集是数据库字符集子集的基础上才能正确转换,否则会出现乱码。

几种常见情况分析
        1. 数据库字符集为Unicode(UTF-8编码)  
        我们的数据库版本是10.2.0.4.0,数据库字符集是:  
        SQL> select * from v$nls_parameters where parameter='NLS_CHARACTERSET';  
          
        PARAMETER                                VALUE  
        ---------------------------------------- ------------------------------  
        NLS_CHARACTERSET               AL32UTF8  
          
        2. 客户端操作系统字符集为代码页936(字符集为ZHS16GBK)  
        可以使用chcp获得windows的代码页(code page)  
        C:\Documents and Settings\a105024\Desktop>chcp  
        Active code page: 936  
          
        3. 创建测试表  
        SQL> create table test(id number,var varchar2(30));  
          
        Table created.  
          
        4. 插入数据  
        这里在同一个操作系统启动两个session,session1的NLS_LANG设为和数据库字符集一样(即AL32UTF8):  
        C:\Documents and Settings\a105024\Desktop>set nls_lang=Simplified Chinese_China.AL32UTF8  
        连接数据库并插入一条数据:  
        Session_1>insert into test values(1,'中国');  
          
        1 row created.  
          
        Session_1>commit;  
          
        Commit complete.  
          
        session2的NLS_LANG设为和客户端操作系统一样(即ZHS16GBK):  
        C:\Documents and Settings\a105024\Desktop>set nls_lang=Simplified Chinese_China.ZHS16GBK  
        连接数据库并插入一条数据:  
        Session_2>insert into test values(2,'中国');  
          
        1 row created.  
          
        Session_2>commit;  
          
        Commit complete.  
          
        5. 执行查询  
        在session 1上执行查询:  
        Session_1>select * from test;  
          
                ID VAR  
        ---------- ---------------------  
                 1 中国  
                 2 涓   浗  
        在session 2上执行查询:  
        Session_2>select * from test;  
          
                ID VAR  
        ---------- --------------------  
                 1 ???  
                 2 中国  

上面例子看起来很诡异,session1和2都能正常显示自己插入的字符串,又都不能正常显示对方插入的字符串。为了弄清楚,我们首先得知道数据库里对这两个字符串是怎么存储的。我们可以使用dump函数获得字符在数据库的编码:
    
    SQL> select id,dump(var,1016) from test;  
    ID DUMP(VAR,1016)  
    -- ------------------------------------------------------------  
     1 Typ=1 Len=4 CharacterSet=AL32UTF8: d6,d0,b9,fa  
     2 Typ=1 Len=6 CharacterSet=AL32UTF8: e4,b8,ad,e5,9b,bd  
根据AL32UTF8的编码,“中国”两字的正确编码为(都为3个字节):
中--e4,b8,ad--e5,9b,bd
因此session 1插入的字符串在数据库中的编码是错误的,session 2正确。

场景1:session 1插入,session 1查询,在数据库中存储错误,但显示正确。

    插入过程:
    ”中国“两字在客户端操作系统字符集ZHS16GBK中的编码是”d6,d0,b9,fa",由于NLS_LANG和数据库字符集相同,数据库端对客户端传过来的字符编码不进行任何转换直接存入数据库,因此数据库中存储的编码也是“d6,d0,b9,fa”,
    读取过程:
    数据库端读取的编码是“d6,d0,b9,fa”,由于NLS_LANG和数据库字符集相同,客户端对数据库端传过来的字符编码不进行任何转换直接显示,编码”d6,d0,b9,fa“在客户端操作系统字符集ZHS16GBK对应的汉字为“中国”。
场景2:session 1插入,session 2查询,在数据库中存储错误,显示也错误。
    插入过程和场景1一样,这里就不再累述。
    读取过程:
    数据库端读取的编码是“d6,d0,b9,fa”,由于NLS_LANG和数据库字符集不同,客户端对数据库端传过来的字符编码进行转换,数据库端字符集AL32UTF8里编为“d6,d0,b9,fa”无法在客户端操作系统字符集ZHS16GBK里找到对应的编码,所以只好用?代替。
    
场景3:session 2插入,session 1查询,在数据库中存储正确,但显示错误。
    插入过程:
    ”中国“两字在客户端操作系统字符集ZHS16GBK中的编码是”d6,d0,b9,fa",由于NLS_LANG和数据库字符集不同,Oracle会进行字符编码转换,也就是将字符集ZHS16GBK里“中国”的编码“d6,d0,b9,fa"转换为字符集"AL32UTF8"里”中国“的编码”e4,b8,ad,e5,9b,bd“。
    读取过程:
    数据库端读取的编码是”e4,b8,ad,e5,9b,bd“,由于NLS_LANG和数据库字符集相同,客户端对数据库端传过来的字符编码不进行任何转换直接显示,编码”e4,b8,ad,e5,9b,bd“在客户端操作系统字符集ZHS16GBK对应的汉字为“涓   浗”(原本2个字符,现在变成了3个字符,因为ZHS16GBK的汉字以2个字节编码)。
场景4:session 2插入,session 2查询,在数据库中存储正确,显示也正确。
    插入过程和场景3类似。
    读取过程:
    数据库端读取的编码是”e4,b8,ad,e5,9b,bd“,由于NLS_LANG和数据库字符集不同,客户端对数据库端传过来的字符编码进行转换,数据库端字符集AL32UTF8里”中国“两字的编码”e4,b8,ad,e5,9b,bd“转换成客户端操作系统字符集ZHS16GBK里“中国”两字的编码“d6,d0,b9,fa",并正常显示。

原文:
http://blog.csdn.net/dbanote/article/details/9158367