今天碰到个问题,用户要求可以存储特殊字符,比如上标的2:(²),如果数据库创建的时候使用了UTF8或者UTF16字符集,就没有任何问题了,但数据库创建时指定的字符集是ZHS16GBK,于是做了个小测试:
SQL>create table unicode_test( a varchar2(100), b nvarchar2(100));
Table created.
SQL>insert into unicode_test values('E=MC²','E=MC²');
1 row created.
SQL>commit;
Commit complete.
SQL>select * from unicode_test;
A B
----------------------------------------
E=MC2 E=MC2
可见无论是varchar2还是nvarchar2,直接插入都不行。改成用UNICODE编码的方式: (\0045\003d\004d\0043\00B2 是 "E=MC² "的UTF-16编码, 00B2就是上标² ,注意这里不能使用UTF-8)
SQL>insert into unicode_test values(unistr('\0045\003d\004d\0043\00B2'),unistr('\0045\003d\004d\0043\00B2'));
1 row created.
SQL>commit;
Commit complete.
SQL>select * from unicode_test;
A B
----------------------------------------
E=MC2 E=MC2
E=MC2 E=MC²
存入到nvarchar2类型成功了,varchar2仍然不正确。
数据文件的dump内容如下:
block_row_dump:
tab 0, row 0, @0x1f84
tl: 20 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 5] 45 3d 4d 43 32
col 1: [10] 00 45 00 3d 00 4d 00 43 00 32
tab 0, row 1, @0x1f70
tl: 20 fb: --H-FL-- lb: 0x2 cc: 2
col 0: [ 5] 45 3d 4d 43 32
col 1: [10] 00 45 00 3d 00 4d 00 43 00 b2
可以清楚的看出两条记录在数据库中实际的存储字节,显然只有第二条的B列是符合要求的。
结论:如果数据库创建的时候没有使用UNICODE编码,当需要存储不在字符集中特殊字符的时候只能使用nvarchar2类型,并且必须使用UTF-16编码加unistr函数的方式传入,如果使用java,可以用String.codePointAt来获取字符的UTF-16编码。