我们使用Oracle 10g
和Oracle 11g
。
我们还有一个层来自动编写查询,来自.net编写的伪SQL代码(类似于SqlAlchemy for Python)。
我们的图层当前用单引号'
包装任何字符串,如果包含非ANSI字符,它会自动组成UNISTR
,并将特殊字符写为unicode字节(如\00E0
)
现在我们创建了一个使用以下结构进行多次插入的方法:
INSERT INTO ... (...)
SELECT ... FROM DUAL
UNION ALL SELECT ... FROM DUAL
...
此算法可以组合查询,其中相同的字符串字段有时会传递为'my simple string'
,有时会被包装为UNISTR('my string with special chars like \00E0')
。
描述的条件导致ORA-12704: character set mismatch
。
一种解决方案是使用INSERT ALL
构造,但 非常慢 与现在使用的构造相比。
另一种解决方案是指示我们的图层将N
放在任何字符串前面(已经用UNISTR
包裹的字符串除外)。这很简单。
我只是想知道这是否会对现有查询造成任何副作用。
注意:数据库中的所有字段都是NCHAR
或NVARCHAR2
。
Oracle ref:http://docs.oracle.com/cd/B19306_01/server.102/b14225/ch7progrunicode.htm
答案 0 :(得分:2)
基本上你要问的是,无论是否存在N函数,字符串的存储方式都有区别。
你可以自己检查一下:
SQL> create table test (val nvarchar2(20));
Table TEST created.
SQL> insert into test select n'test' from dual;
1 row inserted.
SQL> insert into test select 'test' from dual;
1 row inserted.
SQL> select dump(val) from test;
DUMP(VAL)
--------------------------------------------------------------------------------
Typ=1 Len=8: 0,116,0,101,0,115,0,116
Typ=1 Len=8: 0,116,0,101,0,115,0,116
你可以看到完全相同所以没有副作用。
这种工作如此精美的原因是因为unicode的优雅
如果您有兴趣,这里有一个很好的视频解释
答案 1 :(得分:2)
我假设您收到错误"ORA-12704: character set mismatch"
,因为您的引号内的数据被视为char但您的字段是nchar,因此使用不同的字符集整理char,一个使用NLS_CHARACTERSET
,另一个使用NLS_NCHAR_CHARACTERSET
}。
当您使用UNISTR
函数时,它会将数据从char
转换为nchar
(在任何情况下也会将编码值转换为字符),就像Oracle docs所说的那样:
“UNISTR以文本字面或表达形式作为其论据 解析为字符数据并以国家字符返回 集“。
使用N
或TO_NCHAR
显式转换值时,只能在NLS_NCHAR_CHARACTERSET
中获取值而不进行解码。如果您有一些像这样"\00E0"
编码的值,它们将不会被解码,并且将被视为未更改。
因此,如果你有一个插入,如:
insert into select N'my string with special chars like \00E0',
UNISTR('my string with special chars like \00E0') from dual ....
第一个插入字段中的数据将是:'my string with special chars like \00E0'
而不是'my string with special chars like à'
。这是我所知道的唯一副作用。其他查询应该已经使用NLS_NCHAR_CHARACTERSET编码,因此使用显式转换不应该有任何问题。
顺便说一下,为什么不将所有值都插入N'my string with special chars like à'
?如果你在'上层'软件中使用不同的编码,只需将它们编码为UTF-16(我假设你使用UTF-16作为nchars)。
答案 2 :(得分:-1)
如果您有机会更改数据库的字符集,那将真正让您的生活更轻松。我正在研究庞大的生产系统,并发现这样的趋势:由于存储空间便宜,只需每个人都移动到AL32UTF8,国际化的麻烦慢慢成为过去痛苦的回忆。
我发现最简单的方法是使用AL32UTF8作为数据库实例的字符集,并且只需在任何地方使用varchar2。我们通过JDBC读取和编写标准Java unicode字符串作为绑定变量而没有任何伤害,并且小提琴。
构建大量SQL插入文本的想法可能由于多种原因而无法很好地扩展:
您尝试实现的是大量插入。使用Oracle驱动程序的JDBC批处理模式以光速执行该操作,请参阅例如:http://viralpatel.net/blogs/batch-insert-in-java-jdbc/
请注意,插入速度也受触发器(必须执行)和外键约束(必须经过验证)的影响。因此,如果您要插入超过几千行,请考虑禁用触发器和外键约束,并在插入后启用它们。 (您将失去触发器调用,但插入后的约束验证可能会产生影响。)
还要考虑回滚段大小。如果您要插入一百万条记录,则需要一个巨大的回滚段,这可能会导致严重的存储介质交换。在每1000条记录之后提交是一个很好的经验法则。
(Oracle使用版本控制而不是共享锁,因此具有未提交更改的表始终可用于读取.1000条记录提交率意味着每秒大约1次提交 - 速度足以使写入缓冲区受益,但速度足够快,不会干扰与其他人愿意更新相同的表格。)