Oracle数据库中文乱码问题分析与解决
最近在完成一个node项目,其中有一个模块需要从oracle数据库中读取数据。在使用 node-oracledb 连接数据库并做查询后,发现结果中的中文字符都是乱码,而通过oracle的PL/SQL软件执行查询时,结果是正常的。由于历史遗留问题,数据库的字符集编码是US7ASCII,而且这是一个生产环境的数据库,相当多的系统工作在这个数据库上,因此无法改变字符集,只能在程序中寻找方法了。
51cto 上的一篇文章分析了为什么在PL/SQL中汉字能够正确的显示,引用里面的一段分析吧:
正常情况下,要将汉字存入数据库,数据库字符集必须支持中文,而将数据库字符集设置为US7ASCII等单字节字符集是不合适的。US7ASCII字符集只定义了128个符号,并不支持汉字。另外,如果在SQL*PLUS中能够输入中文,操作系统缺省应该是支持中文的,但如果在NLS_LANG中的字符集设置为US7ASCII,显然也是不正确的,它没有反映客户端的实际情况。
但在实际应用中汉字显示却是正确的,这主要是因为Oracle检查数据库与客户端的Oracle字符集设置是同样的,那么数据在客户与数据库之间的存取过程中将不发生任何转换,但是这实际上导致了数据库标识的字符集与实际存入的内容是不相符的。
(因为对oracle很不熟悉,接下来的步骤是一步步探索来的,对其中某些原理的理解可能有所偏差。)
首先,我的查询语句类似于:
1 | select USERNAME, NAME, DEPT from USERS where USERNAME='xxx'; |
查询结果中的name字段是中文,由于oracle以US7ASCII(ISO-8859-1)形式存储和返回了数据,而客户端(node-oracledb)没有相应的转码,因此出现了问题。
在node-oracledb的API中,没有找到相关的配置,尝试设置NLS_LANG环境变量也不起作用。在寻找解决方法的过程中,发现了这个函数: UTL_RAW.CAST_TO_RAW ,能够将VARCHAR2类型的数据转换成原始的字节编码。得到原始字节编码后,可以考虑在程序中进行转换了。
那么,新的查询语句:
1 | select USERNAME, UTL_RAW.CAST_TO_RAW(NAME), DEPT from USERS where USERNAME='xxx'; |
拿到了name字段的原始值后,在网上找了个 中文编码查询 的服务,匹配了一下正确的汉字,发现原始值是结果的GB2312编码,拿 iconv-lite转换一下,搞定。
1 | let result=oracleConnection.execute(sql); |