主键 - 相同的 Oracle 数据库设置:仅其中一个设置异常



edit:查看此问题的末尾,了解导致错误的原因以及我是如何发现的。

当我运行一个将数据批量插入 oracle 数据库的应用程序时,我从 Hibernate 中抛出了一个非常奇怪的异常。该错误来自 Oracle 数据库 ORA-00001,该数据库

"表示已尝试 插入包含重复项的记录 (唯一)键。此错误也将 如果现有记录是 更新以生成重复项 (唯一)钥匙。

这个错误很奇怪,因为我在另一台机器上创建了相同的表(完全相同的定义),如果我通过我的应用程序使用它,我不会得到相同的错误。 并且所有数据都入到数据库中,因此没有真正拒绝任何东西。

两种设置之间必须有所不同,但我唯一能看到的不同之处在于我在发布时获得的横幅输出

select * from v$version where banner like 'Oracle%';

给我带来麻烦的数据库:
Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - Prod有效的那个: Oracle Database 10g Release 10.2.0.3.0 - 64bit Production

表定义、输入和我编写的应用程序对于两者是相同的。所涉及的表基本上是一个带有复合 ID (serviceid、date、value1、value2) 的四列表 - 没有什么花哨的。

关于可能出错的任何想法?我已经多次干净开始,删除两个表以在相同的基础上开始,但我仍然从数据库中收到错误。

更多输出:

Caused by: java.sql.BatchUpdateException: ORA-00001: unique constraint (STATISTICS.PRIMARY_KEY_CONSTRAINT) violated
    at oracle.jdbc.driver.DatabaseError.throwBatchUpdateException(DatabaseError.java:367)
    at oracle.jdbc.driver.OraclePreparedStatement.executeBatch(OraclePreparedStatement.java:8728)
    at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:70)

我如何找出导致问题的原因

多亏了APC和ik_zelf我才能查明此错误的根本原因。事实证明,Quartz 调度程序为生产数据库(错误出现的地方)配置错误。对于针对非故障 oracle 服务器运行的作业,我<cronTriggerExpression>0/5 * * * * ?</cronTriggerExpression>每五秒运行一次批处理作业。我认为一分钟一次对于另一个oracle服务器来说就足够了,并将石英调度程序设置为* */1 * * * ?。事实证明这是错误的,而不是每分钟运行一次,而是每秒运行一次!

每个作业大约需要 1.5-2 秒,因此两个或多个作业同时运行,从而导致服务器上同时插入。因此,我没有插入 529 个元素,而是插入了 1000 到 2000 个元素。将 crontrigger 表达式更改为与另一个表达式相同,每五秒运行一次,解决了问题。

为了找出问题所在,我必须在休眠中设置为 true.cfg.xml并禁用表上的主键约束。

-- To catch exceptions
-- to find the offending rows run the following query
-- SELECT * FROM uptime_statistics, EXCEPTIONS WHERE MY_TABLE.rowid = EXCEPTIONS.row_id;
create table exceptions(row_id rowid,
                        owner varchar2(30),
                        table_name varchar2(30),
                        constraint varchar2(30));
-- This table was set up
CREATE TABLE MY_TABLE
  (
    LOGDATE DATE NOT NULL,
    SERVICEID           VARCHAR2(255 CHAR) NOT NULL,
    PROP_A   NUMBER(10,0),
    PROP_B NUMBER(10,0),
    CONSTRAINT PK_CONSTRAINT PRIMARY KEY (LOGDATE, SERVICEID)
  );
-- Removed the constraint to see what was inserted twice or more
alter table my_table
  disable constraint PK_CONSTRAINT;
-- Enable this later on to find rows that offend the constraints
alter table my_table
  enable constraint PK_CONSTRAINT
    exceptions into exceptions;

您有一个唯一的复合约束。 ORA-00001 表示您有两行或更多行在 ServiceID、日期、值 1 和/或值 2 中具有重复值。 您说两个数据库的输入相同。 因此,请执行以下任一操作:

  • 您正在想象您的程序正在投掷ORA-00001
  • 您误以为两次运行中的输入都相同。

更可能的解释是第二种:一个或多个键列由外部源或默认值填充(例如,ServiceId 的代码表或日期列的 SYSDATE)。 在失败的数据库中,此自动填充无法提供唯一值。 出现这种情况的原因可能有很多,具体取决于您使用的机制。 请记住,在唯一的复合键中,NULL 条目计数。 也就是说,您可以有任意数量的记录(空,空。空,空),但只有一个 (42,空,空,空)。

我们很难猜测实际问题可能是什么,对你来说几乎同样困难(尽管你确实有作为代码作者的优势,这应该给你一些洞察力)。 您需要的是一些跟踪语句。 我的首选解决方案是使用批量DML异常处理,但我是PL/SQL的粉丝。 Hibernate允许您将一些日志记录挂接到您的程序:我建议你打开它。 当代码具有不错的检测时,调试起来要容易得多。

作为最后的手段,请在运行批处理插入之前禁用约束。 之后像这样重新启用它:

alter table t42
    enable constraint t42_uk
        exceptions into my_exceptions
/

如果您有重复的行,这将失败,但至关重要的是,MY_EXCEPTIONS表将列出所有冲突的行。 这至少会给你一些关于重复来源的线索。 如果您还没有异常表,则必须运行脚本:$ORACLE_HOME/rdbms/admin/utlexcptn.sql(您可能需要DBA才能访问此目录)。


博士

见解需要信息:检测代码。

有问题的是一个EE,另一个看起来像一个SE数据库。我希望第一个是在更快的硬件上。如果是这种情况,并且您的日期列是使用 SYSDATE 填充的,则很可能是时间分辨率不够;您获得重复的日期值。如果数据的其他列也不唯一,则会得到 ORA-00001。

这是一个很长的镜头,但乍一看我会朝这个方向看。

是否可以使用异常表来标识数据?请参阅报告约束异常

我的猜测是服务 ID。 休眠service_id用于"新鲜"插入的任何内容都已被使用。

可能该表在一个数据库中为空,但在另一个数据库中填充

我敢打赌,尽管service_id是序列生成的,并且序列号与数据内容不同步。所以你在表中有相同的 1000 行,但正在

SELECT service_id_seq.nextval FROM DUAL

在一个数据库中给出的数字比另一个数据库低。我经常看到这种情况,其中已创建序列(例如出于源代码控制)并且数据已从另一个数据库导入到表中。

最新更新