【问题标题】:ORA-01882 after creating function based index with TIMESTAMP expression from dbms_metadata.get_ddlORA-01882 使用 dbms_metadata.get_ddl 中的 TIMESTAMP 表达式创建基于函数的索引后
【发布时间】:2015-06-28 15:08:32
【问题描述】:

我们有一个很大的现有脚本,可以在我们的客户数据库(克隆)中删除和重新创建表。我们的客户可能稍微更改了表或索引定义,因此我们的脚本尝试使用来自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 的参数,对包含时间戳的索引运行一些额外的查询——无论什么都能完成。

【问题讨论】:

  • 我是 SQL Server 人员,而不是 Oracle,但您不能只截断表而不是删除和重新创建吗?然后你保留所有的表和索引定义。
  • 好点 Jen R。我们的任务比描述的更复杂 - 我们需要删除 MOST,但不是删除数据以回收空间,但仍然留下一些必需品。所以单独截断并不能解决问题 - 我们必须重写脚本以使用复制 + 截断 + (re)insert 设计。
  • 索引函数 NVL(a, TIMESTAMP '2010-01-02 03:04:05') 对我来说没有任何意义:你想用这个索引来实现什么?
  • 数据库处理了许多专门包含NVL(a, TO_DATE('2999-12-31 23:59:59','YYYY-MM-DD HH24:MI:SS') 之类的表达式的查询,据我了解,添加了包含这些常用表达式的索引,因为没有它们,查询计划很糟糕.至于为什么查询本身看起来像这样,一般的想法只是将null 时间戳视为“遥远的未来”,但除此之外我无法回答 - 数据库是多年前设计的。
  • 似乎是 Oracle 支持的一个案例。在我的数据库上它可以正常工作,没有任何错误。

标签: database oracle timezone indices


【解决方案1】:

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

最好使用TO_TIMESTAMP('2010-01-02 03:04:05','YYYY-MM-DD HH24:MI:SS')TIMESTAMP '2010-01-02 03:04:05',它们是一样的,只是写成文字。

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

【讨论】:

  • describe 将数据类型显示为 TIMESTAMP(3) - 我认为这意味着没有时区。
  • 至于在创建索引时使用to_timestamp - 这听起来像是/正确的做法,但客户数据库已经在运行,所以它不在我们的选择范围内。
【解决方案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 是否具有损坏的索引。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-26
    • 2014-08-21
    • 1970-01-01
    相关资源
    最近更新 更多