使用预编译语句处理 Oracle 数据库,出现唯一约束冲突。

huangapple go评论77阅读模式
英文:

Use prepare statement for Oracle Db, get unique constraint violated

问题

我们尝试使用Java的PreparedStatement进行插入操作,但是遇到了ORA-00001:违反唯一约束(POSTIONS_TABLE_PK)的错误。

该应用程序针对Oracle数据库,表名为info:

postion_table 表的列:

 position_id 数字 主键,
 amount 数字,
 account_id 数字

我们需要使 position_id 成为连续的数字,从1开始,依次为 1、2、3 .... n。例如,当我们删除 position_id 为10的记录时,下一个新插入的记录需要使用10作为 position_id。

准备好的语句插入查询如下:

BEGIN
DECLARE
position_id 数字;
BEGIN
  SELECT NVL(MAX(t.position_id) + 1, 1)
    INTO position_id
    FROM  positions_table t
   WHERE account_id = 1;
INSERT INTO positions_table(position_id, amount, account_id)
 VALUES(position_id, ?, ?);
EXCEPTION
  WHEN OTHERS THEN 
    raise_application_error(-20999, '错误: ' || sqlerrm);
    ROLLBACK;
END;
END;

当我们尝试批量插入多条记录时,出现了唯一约束(TPOSTIONS_TABLE_PK)冲突的错误。

对于如何构建准备好的SQL语句,有什么建议吗?

英文:

We try to use java preparedstatement to do insert action, we got ORA-00001: unique constraint (POSTIONS_TABLE_PK) violated error.

The app target at Oracle DB. the table name info:

postion_table columns:

 position_id number pk,
 amount number,
 account_id number

We need postion_id to be sequential number. start from 1. so will be 1, 2 ,3 .... n. For example, when we delete record with position_id 10, then the next new insert need to use 10 as position_id.

The insert query for prepared statement looks like:

BEGIN
DECLARE
position_id number;
BEGIN
  SELECT NVL(MAX(t.position_id) + 1, 1)
    INTO position_id
    FROM  positions_table t
   WHERE account_id = 1;
INSERT into positions_table(position_id,amount, account_id)
 values(position_id, ?, ?);
EXCEPTION
  WHEN OTHERS THEN 
    raise_application_error(-20999, 'Error : ' || sqlerrm);
    ROLLBACK;
END;
END;

We got unique constraint (TPOSTIONS_TABLE_PK) violated error when we try to insert
multiple records in a batch.

Any suggestion on how to build the prepared SQL.

答案1

得分: 1

使用

SELECT NVL(MAX(t.position_id) + 1, 1)

在任何有多个用户的系统中都不起作用。因为两个用户都会运行该命令,他们都会发现POSITION_ID为(假设)17,然后他们都会尝试使用18,结果其中一个会失败。

使用序列(可能会产生间隔),但是当您希望获得一个没有间隔数字的行列表时,在查询时执行,即

select row_number() over ( partition by ... order by position_id )

如果您真的非常需要在插入时生成无间隔序列,那么一个相对可扩展的方法是:

1)创建一个包含所有数字的表(例如,从1到1,000,000),并对该列创建索引。
2)每个想要序列的人都会执行

open cursor for select ... from that_table for update skip locked;

然后从中获取一行。

随着时间的推移,每个数字都将被使用。但对于通常并不是真正业务需求的事情来说,这需要很多工作和复杂性。

英文:

Using

SELECT NVL(MAX(t.position_id) + 1, 1)

will never work in any system where you have more than a single user. Because two users will both run the command, they will both find POSITION_ID to be (say) 17, and they will both then try use 18, and one will fail.

Use a sequence (which might give you gaps) but then when you wish to get a list of rows with a gap-free number, do it at query time, ie

select row_number() over ( partition by ... order by position_id )

If you really really really need a gap free sequence generated at INSERT time, then a reasonably scalable method is:

  1. create table with all the numbers (eg, 1 through 1,000,000) and index that column.

  2. each person wanting a sequence does

    open cursor for select ... from that_table for update skip locked;

and fetches a row from it.

Over time, every number will be used. But its a lot of work and complexity for something that is often not really a business requirement.

huangapple
  • 本文由 发表于 2020年7月23日 14:37:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/63048280.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定