0

我刚刚找到以下代码:

select max(id) from TABLE_NAME ...

... do some stuff ...

insert into TABLE_NAME (id, ... )
VALUES (max(id) + 1, ...)

我可以为 PK 创建一个序列,但有一堆现有代码(经典 asp、不属于该项目的现有 asp.net 应用程序)不会使用它。

我应该忽略它,还是有办法在不进入现有代码的情况下修复它?

我认为最好的选择就是这样做:

insert into TABLE_NAME (id, ... )
VALUES (select max(id) + 1, ...)

选项?

4

6 回答 6

2

您可以在表上创建一个触发器,用您从序列中获取的值覆盖 ID 的值。这样,您仍然可以使用其他现有代码并且并发插入没有问题。

如果您无法更改其他软件并且他们仍然执行最不幸的选择 max(id)+1 插入。然后你可以做的是:

对于您自己的插入,请使用序列并使用 -1*(序列值)填充 ID 字段。这样插入既不会干扰现有程序,也不会与现有程序冲突。(在没有 id 值的情况下进行插入,并使用触发器用序列的负值填充 ID)。

于 2009-02-19T15:28:05.993 回答
1

这很快就变成了对应用程序架构的讨论,尤其是当问题归结为“我应该做什么?”时。

Oracle 中的主键确实需要来自序列,并且由于您在应用程序代码中处理复杂的插入逻辑(至少是父/子插入),因此您应该进入现有代码,正如您所说(因为触发器可能获胜帮不了你)。

在一种极端情况下,您可以从应用程序中移除直接 SQL 访问,并让它们调用服务,以便可以集中插入/更新/删除代码。或者你可以使用某种 MVC 架构重写你的代码。我假设两者都对你的情况来说太过分了。

id 列是否至少设置为真正的主键,所以有一个约束可以防止重复发生?如果没有,从那里开始。

一旦主键到位,或者如果它已经到位,插入开始失败只是时间问题;你会知道他们什么时候开始失败,对吧?如果没有,请继续错误记录。

现在修复应用程序代码。当你在那里时,你至少应该编写和调用帮助代码,这样你的数据库交互就在尽可能少的地方。然后为其他开发人员提供一些领导,并确保他们也使用帮助代码。

于 2009-02-19T22:40:50.260 回答
1

正如其他人所说,您可以使用序列覆盖数据库触发器中的最大值。但是,如果任何应用程序代码像这样使用该值,则可能会导致问题:

select max(id) from TABLE_NAME ...

... do some stuff ...

insert into TABLE_NAME (id, ... )
VALUES (max(id) + 1, ...)

insert into CHILD_TABLE (parent_id, ...)
VALUES (max(id) + 1, ...)
于 2009-02-19T15:47:34.363 回答
1

在插入行前触发器中使用序列。select max(id) + 1 在多中心环境中不起作用。

于 2009-02-19T19:16:20.973 回答
0

可以通过获取变量中的最大值然后将其插入表中来完成

声明 v_max int; 从表中选择 max(id) 到 v_max 中;

插入表值((v_max+rownum),val1,val2....,valn); 犯罪;

这将在单个插入和批量插入中创建一个序列。

于 2013-02-18T09:12:57.733 回答
0

大问题:有人依赖PK的价值吗?如果不是,我建议使用触发器,从序列中获取 id 并设置它。插入根本不会指定和标识。

我不确定,但

插入 TABLE_NAME (id, ... ) VALUES (select max(id) + 1, ...)

当会话到达该代码时可能会导致问题。可能是 oracle 读取表(计算 max(id)),然后尝试获取 PK 上的锁定以进行插入。如果是这种情况,两个并发会话可能会尝试使用相同的 id,从而导致第二个会话出现异常。

您可以向触发器添加一些日志记录,以检查是否已处理已设置 ID 的插入。所以你知道你仍然需要寻找一些使用旧代码的地方。

于 2009-02-19T15:32:25.877 回答