N功能可以导致现有查询出现问题吗?

时间:2016-03-03 13:54:12

标签: oracle unicode bulkinsert

我们使用Oracle 10gOracle 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包裹的字符串除外)。这很简单。

我只是想知道这是否会对现有查询造成任何副作用。

注意:数据库中的所有字段都是NCHARNVARCHAR2

Oracle ref:http://docs.oracle.com/cd/B19306_01/server.102/b14225/ch7progrunicode.htm

3 个答案:

答案 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的优雅

如果您有兴趣,这里有一个很好的视频解释

https://www.youtube.com/watch?v=MijmeoH9LT4

答案 1 :(得分:2)

我假设您收到错误"ORA-12704: character set mismatch",因为您的引号内的数据被视为char但您的字段是nchar,因此使用不同的字符集整理char,一个使用NLS_CHARACTERSET,另一个使用NLS_NCHAR_CHARACTERSET }。

当您使用UNISTR函数时,它会将数据从char转换为nchar(在任何情况下也会将编码值转换为字符),就像Oracle docs所说的那样:

  

“UNISTR以文本字面或表达形式作为其论据   解析为字符数据并以国家字符返回   集“。

使用NTO_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)

  • 使用n功能 - 你已经有了上面的答案。

如果您有机会更改数据库的字符集,那将真正让您的生活更轻松。我正在研究庞大的生产系统,并发现这样的趋势:由于存储空间便宜,只需每个人都移动到AL32UTF8,国际化的麻烦慢慢成为过去痛苦的回忆。

我发现最简单的方法是使用AL32UTF8作为数据库实例的字符集,并且只需在任何地方使用varchar2。我们通过JDBC读取和编写标准Java unicode字符串作为绑定变量而没有任何伤害,并且小提琴。

构建大量SQL插入文本的想法可能由于多种原因而无法很好地扩展:

  • 有一个固定长度的最大允许SQL语句 - 所以它不能使用10000个插入
  • 建议使用绑定变量(然后你不会有n' xxx' vs unistr mess)。
  • 动态创建新SQL语句的想法非常缺乏资源。它不允许Oracle为任何事情缓存任何执行计划,并且会使Oracle在每次调用时都难以解析您的looong语句。

您尝试实现的是大量插入。使用Oracle驱动程序的JDBC批处理模式以光速执行该操作,请参阅例如:http://viralpatel.net/blogs/batch-insert-in-java-jdbc/

请注意,插入速度也受触发器(必须执行)和外键约束(必须经过验证)的影响。因此,如果您要插入超过几千行,请考虑禁用触发器和外键约束,并在插入后启用它们。 (您将失去触发器调用,但插入后的约束验证可能会产生影响。)

还要考虑回滚段大小。如果您要插入一百万条记录,则需要一个巨大的回滚段,这可能会导致严重的存储介质交换。在每1000条记录之后提交是一个很好的经验法则。

(Oracle使用版本控制而不是共享锁,因此具有未提交更改的表始终可用于读取.1000条记录提交率意味着每秒大约1次提交 - 速度足以使写入缓冲区受益,但速度足够快,不会干扰与其他人愿意更新相同的表格。)