字符集的概念

Oracle字符集是一个字节数据的符号集合,有大小之分,有相互的包容关系。Oracle支持国家语言的体系结构,允许你使用本地化语言来存储,处理,检索数据。它使数据库工具,错误消息,排序次序,日期,时间,货币,数字,和日历自动适应本地化语言和平台。

影响Oracle数据库字符集最重要的参数是NLS_LANG参数。格式:NLS_LANG =language_territory.charset

其中:Language 指定服务器消息的语言,territory 指定服务器的日期和数字格式,charset 指定字符集。如:AMERICAN_AMERICA、ZHS16GBK。从NLS_LANG的组成我们可以看出,真正影响数据库字符集的其实是第三部分。所以两个数据库之间的字符集只要第三部分一样就可以相互导入导出数据,前面影响的只是提示信息是中文还是英文。

Oracle的字符集有互相的包容关系。如us7ascii就是zhs16gbk的子集,从us7ascii到zhs16gbk不会有数据解释上的问题,不会有数据丢失。在所有的字符集中utf8应该是最大,因为它基于unicode,双字节保存字符(也因此在存储空间上占用更多)。

一旦数据库创建后,数据库的字符集理论上讲是不能改变的。字符集的转换是从子集到超集受支持,反之不行。如果两种字符集之间根本没有子集和超集的关系,那么字符集的转换是不受Oracle支持的。一般来说,除非万不得已,不建议修改Oracle数据库server端的字符集。特别说明,最常用的两种字符集ZHS16GBK和ZHS16CGB231280之间不存在子集和超集关系,因此理论上讲,这两种字符集之间的相互转换不受支持。

查询字符集

查询oracle server端的字符集

select userenv('language') from dual;

USERENV('LANGUAGE')
--------------------------------------------------------------------------------
AMERICAN_AMERICA.ZHS16GBK
select * from v$nls_parameters;

PARAMETER                      VALUE                                        CON_ID
------------------------------ ---------------------------------------- ----------
NLS_LANGUAGE                   AMERICAN                                          1
NLS_TERRITORY                  AMERICA                                           1
NLS_CURRENCY                   $                                                 1
NLS_ISO_CURRENCY               AMERICA                                           1
NLS_NUMERIC_CHARACTERS         .,                                                1
NLS_CALENDAR                   GREGORIAN                                         1
NLS_DATE_FORMAT                DD-MON-RR                                         1
NLS_DATE_LANGUAGE              AMERICAN                                          1
NLS_CHARACTERSET               ZHS16GBK                                          1
NLS_SORT                       BINARY                                            1
NLS_TIME_FORMAT                HH.MI.SSXFF AM                                    1

PARAMETER                      VALUE                                        CON_ID
------------------------------ ---------------------------------------- ----------
NLS_TIMESTAMP_FORMAT           DD-MON-RR HH.MI.SSXFF AM                          1
NLS_TIME_TZ_FORMAT             HH.MI.SSXFF AM TZR                                1
NLS_TIMESTAMP_TZ_FORMAT        DD-MON-RR HH.MI.SSXFF AM TZR                      1
NLS_DUAL_CURRENCY              $                                                 1
NLS_NCHAR_CHARACTERSET         AL16UTF16                                         1
NLS_COMP                       BINARY                                            1
NLS_LENGTH_SEMANTICS           BYTE                                              1
NLS_NCHAR_CONV_EXCP            FALSE                                             1

19 rows selected.

查询dmp文件的字符集

用Oracle的expdp工具导出的dmp文件也包含了字符集信息。如果dmp文件不大,比如只有几M或几十M,可以用UltraEdit打开(16进制方式)。如下图:

Oracle-修改字符集_数据库

如果dmp文件很大,比如有2G以上(这也是最常见的情况),用文本编辑器打开很慢或者完全打不开,可以用以下命令(在Linux主机上):

hexdump -C ora_baoyw_file.dmp | more

Oracle-修改字符集_字符集修改_02

16进制转换

> select to_char(nls_charset_id('ZHS16GBK'),'xxxx') from dual;

TO_CHAR(NLS_CHA
---------------
  354

也可以不打开,用以下脚本:

cat ora_baoyw_file.dmp |od -x|head -1|awk '{print $2 $3}'|cut -c 3-6

修改字符集

RAC-修改实例字符集

RAC环境下,修改字符集时,需要关闭全部节点。然后打开节点1,在节点1下操作即可。

SYS@b19c01> startup
ORACLE instance started.

Total System Global Area 2734682488 bytes
Fixed Size                  9138552 bytes
Variable Size             553648128 bytes
Database Buffers         2164260864 bytes
Redo Buffers                7634944 bytes
Database mounted.
Database opened.
SYS@b19c01> select userenv('language') from dual;

USERENV('LANGUAGE')
----------------------------------------------------
AMERICAN_AMERICA.AL32UTF8

SYS@b19c01> show parameter cluster_database

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
cluster_database                     boolean     TRUE
cluster_database_instances           integer     2
SYS@b19c01> alter system set cluster_database=FALSE sid='*' scope=spfile;

System altered.

SYS@b19c01> show parameter cluster_database

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
cluster_database                     boolean     TRUE
cluster_database_instances           integer     2
SYS@b19c01> shutdown immediate
Database closed.
Database dismounted.
ORACLE instance shut down.
SYS@b19c01>
SYS@b19c01> startup nomount
ORACLE instance started.

Total System Global Area 2734682488 bytes
Fixed Size                  9138552 bytes
Variable Size             553648128 bytes
Database Buffers         2164260864 bytes
Redo Buffers                7634944 bytes
SYS@b19c01> alter database mount exclusive;

Database altered.

SYS@b19c01> alter system enable restricted session;

System altered.

SYS@b19c01> alter system set job_queue_processes=0;

System altered.

SYS@b19c01> alter system set aq_tm_processes=0;

System altered.

SYS@b19c01> alter database open;

Database altered.

SYS@b19c01> show parameter cluster_database

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
cluster_database                     boolean     FALSE
cluster_database_instances           integer     1
SYS@b19c01>
SYS@b19c01> alter database character set internal_use ZHS16GBK;

Database altered.

SYS@b19c01> shutdown immediate
Database closed.
Database dismounted.
ORACLE instance shut down.
SYS@b19c01> startup
ORA-01102: cannot mount database in EXCLUSIVE mode
SYS@b19c01>
SYS@b19c01> startup
ORA-01102: cannot mount database in EXCLUSIVE mode
SYS@b19c01>
SYS@b19c01> startup
ORACLE instance started.

Total System Global Area 2734682488 bytes
Fixed Size                  9138552 bytes
Variable Size             553648128 bytes
Database Buffers         2164260864 bytes
Redo Buffers                7634944 bytes
Database mounted.
Database opened.
SYS@b19c01>
SYS@b19c01> select userenv('language') from dual;

USERENV('LANGUAGE')
--------------------------------------------------------------------------------
AMERICAN_AMERICA.ZHS16GBK

SYS@b19c01> set linesize 200;
SYS@b19c01> col parameter for a30;
SYS@b19c01> col value for a40;
SYS@b19c01> select * from v$nls_parameters;

PARAMETER                      VALUE                                        CON_ID
------------------------------ ---------------------------------------- ----------
NLS_LANGUAGE                   AMERICAN                                          1
NLS_TERRITORY                  AMERICA                                           1
NLS_CURRENCY                   $                                                 1
NLS_ISO_CURRENCY               AMERICA                                           1
NLS_NUMERIC_CHARACTERS         .,                                                1
NLS_CALENDAR                   GREGORIAN                                         1
NLS_DATE_FORMAT                DD-MON-RR                                         1
NLS_DATE_LANGUAGE              AMERICAN                                          1
NLS_CHARACTERSET               ZHS16GBK                                          1
NLS_SORT                       BINARY                                            1
NLS_TIME_FORMAT                HH.MI.SSXFF AM                                    1

PARAMETER                      VALUE                                        CON_ID
------------------------------ ---------------------------------------- ----------
NLS_TIMESTAMP_FORMAT           DD-MON-RR HH.MI.SSXFF AM                          1
NLS_TIME_TZ_FORMAT             HH.MI.SSXFF AM TZR                                1
NLS_TIMESTAMP_TZ_FORMAT        DD-MON-RR HH.MI.SSXFF AM TZR                      1
NLS_DUAL_CURRENCY              $                                                 1
NLS_NCHAR_CHARACTERSET         AL16UTF16                                         1
NLS_COMP                       BINARY                                            1
NLS_LENGTH_SEMANTICS           BYTE                                              1
NLS_NCHAR_CONV_EXCP            FALSE                                             1

19 rows selected.

SYS@b19c01>
startup
##查询当前字符集
select userenv('language') from dual;
##设置参数cluster_database=FALSE
alter system set cluster_database=FALSE scope=spfile sid='*';
##关闭数据库
shutdown immediate
##启动数据库到nomount
startup nomount
##启动数据库到mount exclusive模式
alter database mount exclusive;
##启用受限会话
alter system enable restricted session;
##设置作业队列进程为0
alter system set job_queue_processes=0;
##设置aq_tm_processes=0 此参数监控队列消息的时间, 并控制处理具有指定延迟和过期属性的消息。
alter system set aq_tm_processes=0;
##打开数据库
alter database open;
##设置字符集 internal_use跳过子集与超集的检验修改字符集
alter database character set internal_use ZHS16GBK;
##恢复参数cluster_database=TRUE
alter system set cluster_database=TRUE sid='*' scope=spfile;
##恢复消息队列参数为默认值
alter system set job_queue_processes=1000;
alter system set aq_tm_processes=1;
##关闭
shutdown immediate
##启动
startup

####修改完成后,以下3个参数,改回原值
##在单机环境下 参数 cluster_database 不需要做修改。
##参数 cluster_database 如果为 FALSE,在集群环境下,只能启动一个节点
##否则会报错 ORA-01102: cannot mount database in EXCLUSIVE mode
alter system set cluster_database=TRUE sid='*' scope=spfile;
##以下两个参数关系到消息队列,也需修改为默认值
alter system set job_queue_processes=1000;
alter system set aq_tm_processes=1;

RAC-修改PDB字符集

SYS@b19c01> show pdbs

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO
         3 PDB1                           READ WRITE NO
         4 PDB2                           MOUNTED
SYS@b19c01> alter session set container=pdb1;

Session altered.

SYS@b19c01> select userenv('language') from dual;

USERENV('LANGUAGE')
----------------------------------------------------
AMERICAN_AMERICA.AL32UTF8

SYS@b19c01> alter pluggable database pdb1 close immediate;

Pluggable database altered.

SYS@b19c01> alter pluggable database pdb1 open read write restricted;

Pluggable database altered.

SYS@b19c01> select userenv('language') from dual;

USERENV('LANGUAGE')
----------------------------------------------------
AMERICAN_AMERICA.AL32UTF8

SYS@b19c01> alter database character set internal_use ZHS16GBK;
alter database character set internal_use ZHS16GBK
*
ERROR at line 1:
ORA-65025: Pluggable database PDB1 is not closed on all instances.


SYS@b19c01> alter database character set internal_use ZHS16GBK;

Database altered.

SYS@b19c01> alter pluggable database pdb1 close immediate;

Pluggable database altered.

SYS@b19c01> alter pluggable database pdb1 open;

Pluggable database altered.

SYS@b19c01> select userenv('language') from dual;

USERENV('LANGUAGE')
--------------------------------------------------------------------------------
AMERICAN_AMERICA.ZHS16GBK

SYS@b19c01> set linesize 200;
SYS@b19c01> col parameter for a30;
SYS@b19c01> col value for a40;
SYS@b19c01> select * from v$nls_parameters;

PARAMETER                      VALUE                                        CON_ID
------------------------------ ---------------------------------------- ----------
NLS_LANGUAGE                   AMERICAN                                          3
NLS_TERRITORY                  AMERICA                                           3
NLS_CURRENCY                   $                                                 3
NLS_ISO_CURRENCY               AMERICA                                           3
NLS_NUMERIC_CHARACTERS         .,                                                3
NLS_CALENDAR                   GREGORIAN                                         3
NLS_DATE_FORMAT                DD-MON-RR                                         3
NLS_DATE_LANGUAGE              AMERICAN                                          3
NLS_CHARACTERSET               ZHS16GBK                                          3
NLS_SORT                       BINARY                                            3
NLS_TIME_FORMAT                HH.MI.SSXFF AM                                    3

PARAMETER                      VALUE                                        CON_ID
------------------------------ ---------------------------------------- ----------
NLS_TIMESTAMP_FORMAT           DD-MON-RR HH.MI.SSXFF AM                          3
NLS_TIME_TZ_FORMAT             HH.MI.SSXFF AM TZR                                3
NLS_TIMESTAMP_TZ_FORMAT        DD-MON-RR HH.MI.SSXFF AM TZR                      3
NLS_DUAL_CURRENCY              $                                                 3
NLS_NCHAR_CHARACTERSET         AL16UTF16                                         3
NLS_COMP                       BINARY                                            3
NLS_LENGTH_SEMANTICS           BYTE                                              3
NLS_NCHAR_CONV_EXCP            FALSE                                             3

19 rows selected.

SYS@b19c01>
alter session set container=pdb1;
alter pluggable database pdb1 close immediate;
##打开 PDB 受限状态
alter pluggable database pdb1 open read write restricted;
##修改字符集
alter database character set internal_use ZHS16GBK;
alter pluggable database pdb1 close immediate;
alter pluggable database pdb1 open;
select userenv('language') from dual;

单机-修改字符集

startup mount 
alter system enable restricted session; 
alter system set job_queue_processes=0; 
alter system set aq_tm_processes=0; 
alter database open; 
alter database character set zhs16gbk; 
--ORA-12712: new character set must be a superset of old character set 
--ORA-12712:新字符集必须为旧字符集的超集
alter database character set internal_use zhs16gbk; 
--使用 internal_use 使oracle数据库绕过了子集与超集的校验. 
shutdown immediate
startup

单机-测试实例截图

AL32UTF8 转 ZHS16GBK(正常显示)

Oracle-修改字符集_字符集修改_03

ZHS16GBK 转 AL32UTF8(乱码显示)

Oracle-修改字符集_oracle_04