使用dbms_metadata.get_ddl中的TIMESTAMP表达式创建基于函数的索引后的ORA-01882

时间:2015-04-21 12:40:31

标签: database oracle timezone indices

我们有一个很大的现有脚本,可以删除并重新创建客户数据库(克隆)中的表。我们的客户可能稍微更改了表或索引定义,因此我们的脚本尝试使用dbms_metadata.get_ddl的输出来重新创建表,但是我们遇到了带有时间戳表达式的基于函数的索引的问题。模拟客户表的简约示例:

create table t(a timestamp, b timestamp);
create index idx_ta on t (nvl(a, TO_DATE('2010-01-02 03:04:05','YYYY-MM-DD HH24:MI:SS')));
create index idx_tb on t (nvl(b, TO_DATE('2010-01-02 03:04:05','YYYY-MM-DD HH24:MI:SS')));

我们的脚本尝试通过处理dbms_metadata.get_ddl的输出来查看现有数据库。例如:

select dbms_metadata.get_ddl('INDEX','IDX_TA') from dual;

输出(裁剪):CREATE INDEX "MYUSER"."IDX_TA" ON "MYUSER"."T" (NVL("A",TIMESTAMP' 2010-01-02 03:04:05'))

我们的脚本读取此输出并尝试使用它来重新创建这样的表和索引(我将调用我们的脚本U创建的克隆,以区分重新创建的版本和原始版本):

create table u(a timestamp, b timestamp);
create index idx_ua on u (nvl(a, TIMESTAMP' 2010-01-02 03:04:05'));
create index idx_ub on u (nvl(b, TIMESTAMP' 2010-01-02 03:04:05'));
创建

idx_ua时没有错误消息,但create index idx_ub失败并显示:

SQL Error: ORA-01882: tidszoneregionen  blev ikke fundet
01882. 00000 -  "timezone region not found"

通常,创建idx_ua后所有内容都会失败,例如insert into u values (null,null);失败并显示相同的错误消息。

idx_ua看起来像这样(来自get_ddl的裁剪输出):CREATE INDEX "MYUSER"."IDX_UA" ON "MYUSER"."U" (NVL("A",TIMESTAMP' 2010-01-02 03:04:05,000000000'))

我们尝试执行alter session set nls_timestamp_tz_format=...以确保get_ddl的输出将使用预定的时间戳格式,但它没有任何效果。事实上,get_ddl为不同的索引输出不同的时间戳格式,尽管据我们所知,我们所有的索引都是以相同的方式创建的。我们怀疑它取决于用于创建索引的客户端。这也意味着get_ddl的输出在时间戳方面基本上没用。

我们尝试了Oracle 11和12.此处的示例仅使用SQL Developer。

我们需要的是一种(更可靠)以自动方式删除和重建上述表格的方法。使用get_ddl的替代方法,调整一些影响get_ddl的参数,对包含时间戳的索引运行一些额外的查询 - 无论什么工作都完成。

2 个答案:

答案 0 :(得分:2)

作为解决方法,请在应用索引之前执行以下操作。

alter session set NLS_NUMERIC_CHARACTERS = ',.';

错误是由Oracle错误16731148引起的,并且在您创建涉及时间戳的基于函数的索引之后发生,而您的NLS_NUMERIC_CHARACTERS设置不是',。'。由于NLS设置,该错误导致Oracle错误地在时间戳表示中生成逗号(TIMESTAMP'2010-01-02 03:04:05,000000000'),即使时间戳应该具有与NLS无关的语法。该错误存在于11.2中,并在12.2.0.3中修复。

如果您的数据库已损坏,则必须删除相关索引,然后在设置NLS_NUMERIC_CHARACTERS后重新创建它们,如上所示。如果简单的select 1 from T导致ORA-01882错误,您可以快速确定表T是否有损坏的索引。

答案 1 :(得分:0)

您不应在TO_DATE(...)列上使用TIMESTAMP,这需要隐式广告。

更好地使用相同的TO_TIMESTAMP('2010-01-02 03:04:05','YYYY-MM-DD HH24:MI:SS')TIMESTAMP '2010-01-02 03:04:05',只是用文字书写。

您确定数据类型真的是TIMESTAMP而不是TIMESTAMP WITH TIMEZONE还是TIMESTAMP WITH LOCAL TIMEZONE