这种情况很常见,而且可以举一反三
(一)错误表现
(1)PL/SQL上运行:
SELECT last_name,hire_date,
NEXT_DAY(ADD_MONTHS(hire_date, 6),'Sunday') REVIEW
FROM hr.employees;
运行返回错误:
|
(二)错误分析
(1)查看错误号:
[[email protected] ~]$ oerr ora 01846
01846, 00000, "not a valid day of the week"
// *Cause:
// *Action:
(2)在server端sqlplus上运行
SQL> SELECT last_name,hire_date,
2 NEXT_DAY(ADD_MONTHS(hire_date, 6),'Sunday') REVIEW
3 FROM hr.employees;
LAST_NAME HIRE_DATE REVIEW
------------------------- --------- ---------
OConnell 21-JUN-07 23-DEC-07
Grant 13-JAN-08 20-JUL-08
Whalen 17-SEP-03 21-MAR-04
Hartstein 17-FEB-04 22-AUG-04
Fay 17-AUG-05 19-FEB-06
Mavris 07-JUN-02 08-DEC-02
Baer 07-JUN-02 08-DEC-02
Higgins 07-JUN-02 08-DEC-02
Gietz 07-JUN-02 08-DEC-02
King 17-JUN-03 21-DEC-03
Kochhar 21-SEP-05 26-MAR-06
……..
运行非常正常
|
(3)此时结果是server端sqlplus运行正常,客户端pl/sql显示ora-01846 周中的日无效
,分析一下我们肯定定位错误来源于客户端字符集:
查询服务器ORACLE字符集:
select userenv('language') from dual;
------------------以上查询oracle server端的字符集
NLS_LANG=<language>_<territory>.<client character set>
Language: 显示oracle消息,校验,日期命名
Territory:指定默认日期、数字、货币等格式
Client character set:指定客户端将使用的字符集
此例表示oracle服务端
SIMPLIFIED CHINESE是语言,CHINESE_CHINA是地区,AL32UTF8是客户端字符集 |
----------再查询数据库字符集
select * from nls_database_parameters;
名词解释:
NLS_DATE_LANGUAGE specifies the language to use for the spelling of day and month names and date abbreviations (a.m., p.m., AD, BC) returned by the TO_DATE and TO_CHAR functions.
NLS_LANGUAGE specifies the default language of the database. This language is used for messages, day and month names, symbols for AD, BC, a.m., and p.m., and the default sorting mechanism. This parameter also determines the default values of the parameters NLS_DATE_LANGUAGE and NLS_SORT.
|
此时知道字符集是AL32UTF8,即通用字符集
NLS_DATE_LANGUAGE,即数据库默认日期语言是AMERICAN。
所以sqlplus运行正常。
但客户端错误,我们在客户端运行一句SQL就明白了
select TO_CHAR(ADD_MONTHS(hire_date, 6),'DL') from hr.employees;
客户端数据库默认日期是简体中文,所以在NEXT_DAY()函数时找不到英文的
MONDAY到SUNDAY,这样问题就知道来由了。
验证一下:
select * from NLS_SESSION_PARAMETERS;
客户端的NLSDATE_LANGUAGE与数据库不一致 |
(三)问题解决
问题解决非常简单,因为只与客户端相关,所以修改用户字符集就可以了
这有两个方法:
(1)修改Session级别
alter SESSION set NLS_DATE_LANGUAGE='AMERICAN' ;
---------修改当前Session的NLS_DATE_LANGUAGE
SELECT last_name,hire_date,
NEXT_DAY(ADD_MONTHS(hire_date, 6),'Sunday') REVIEW
FROM hr.employees;
运行正常,但适用于当前session;session变换要重新指定。
|
(2)修改SQL
这种方法比较方便不用次次指定,一劳永逸
具体方法:
SELECT last_name,hire_date,
NEXT_DAY(ADD_MONTHS(hire_date, 6),'星期日') REVIEW
FROM hr.employees;
|
(3)用环境变量修改客户端字符集
说明:
数据库服务器字符集select * from nls_database_parameters,其来源于props$,是表示数据
库的字符集,在创建数据库的时候设定的,一般不会改变.
客户端字符集环境select * from nls_instance_parameters,其来源于v$parameter,表示客户端的字符集的设置,可能是参数文件,环境变量或者是注册表。
v$nls_parameters 显示当前会话值 ,他受客户端nls 的控制。
会话字符集环境 select * from nls_session_parameters,其来源于v$nls_parameters,
表示会话自己的设置,可能是会话的环境变量或者是alter session完成,如果会话没有特殊的
设置,将与nls_instance_parameters一致。
客户端的字符集要求与服务器一致,才能正确显示数据库的非Ascii字符。如果多个设置存
在的时候,alter session>环境变量>注册表>参数文件
字符集要求一致,但是语言设置却可以不同,语言设置建议用英文。如字符集是zhs16gbk
,则nls_lang可以是American_America.zhs16gbk。
|
那么在我们PL/SQL的SESSION未”ALTER SESSION”或修改会话的环境变量时,
nls_session_parameters来源于v$nls_parameters,v$nls_parameters 显示当前会话值 ,他受客户端nls 的控制。
查询以上视图可知:NLS_DATE_LANGUAGE=SIMPLIFIED CHINESE,
故而有三种修改方法环境变量,注册表,参数文件
注册表,参数文件修改比较危险,建议使用环境变量具体方法是
添加一个系统环境变量名为:NLS_DATE_LANGUAGE
值为:AMERICAN
之后重启Session就可以了
(4)用注册表修改客户端字符集
1)UNIX环境
$NLS_DATE_LANGUAGE=“AMERICAN”
$export NLS_DATE_LANGUAGE
或者编辑oracle用户的profile文件,添加export NLS_DATE_LANGUAGE=“AMERICAN”
2)Windows环境 编辑注册表
32位系统
Regedit.exe ---> HKEY_LOCAL_MACHINE --->SOFTWARE ---> ORACLE-HOME
64位系统
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\ORACLE\KEY_OraDb11g_home1
实际中KEY_OraDb11g_home1是KEY_[ORACLE_HOME_NAME],[ORACLE_HOME_NAME]在
注册表中可以查到
修改时找寻值NLS_DATE_LANGUAGE,将其修改为AMERICAN;否则添加一名为NLS_DATE_LANGUAGE的字符串值,其等于AMERICAN,修改完成后客户端机器重启即可。 |
(四)问题注意
在修改NLS_LANGUAGE由简体中文修改为AMERICAN,但会产生汉字的字符截断,而且客户端查询时中文会显示乱码,所以在中文环境中
不建议使用。
相关文档请详见
Oracle 字符集的查看和修改
http://www.cnblogs.com/rootq/articles/2049324.html