1

注意:感谢所有答案,谢谢,但我们已经有一个序列..并且不能使用 UNIQUE 约束,因为某些项目需要重复..我需要以某种方式使用 PLSQL 来处理这个问题,所以基于一些标准(使用if 语句)我需要确保没有重复。只是为了确认,这些标识符是非常定制的,具有不同类型的字符串。对于每组字符串,我们有一个数字,仅用于该字符串(STR-STR-####),我们有数百个这样的 STR-STR 组合,对于每个组合,我们都有一个 #### 计数......在这些之上,一些 STR-STR 组合是允许的有重复..所以我们不能使用唯一约束,我们不能使用主键,因为它不是一个简单的数字,最重要的是我们确实为每个项目分配了一个主键..这些标识符用于用户而不是用于数据库管理。

当用户创建一个项目时,根据一些标准,我们给项目一个特定的数字。在保存函数中,调用一个函数来准备第一个初始字符串,然后扫描保存数字的表并保存为该特定字符串分配下一个可用的 4 位数字。

现在有一些问题,在获取下一个数字和提交到数据库之间大约有 1000 行代码。问题是当两个人在几秒钟内创建一个具有相同标准的项目时,有时会发出相同的数字。

我所做的是,就在提交之前,我检查数据库中的号码,如果它存在,我调用该函数再次获取下一个可用号码......

即使这段代码减少了重复的机会,如果我同时保存 2 个项目,我仍然会得到一个重复的数字..

任何人都知道有什么方法可以避免重复并将重复数字的机会降至 0?

EDIT1:我们已经有一个主键。这个标识符是一个特殊的字符串,并且非常定制,因此不可能只使用从 0 开始计数的数字

EDIT2:在某些情况下我们需要重复..这非常小(可能大约 10 个不同的项目轨道使用重复项)所以在我使用 if 语句提交之前检查重复项之前,所以如果项目保存 dosn' t 属于应该有重复的系统之一,然后我跳过检查...

编辑 3:我们在这里使用 PL/SQL

编辑4:我想这个问题非常具体,我并没有完全传达出来。虽然有很多答案,但没有一个人真正抓住了我的问题。无论如何,我解决了这个问题并在下面添加了我的答案。

4

13 回答 13

5

查找命令 CREATE SEQUENCE。Oracle 可以为您处理唯一编号的生成。(实际上)每个数据库都有处理这个问题的方法,尽管它们在实现方式上有所不同。

[针对编辑成问题的新要求]

您仍然可以使用 SEQUENCE 生成计数部分,然后将其与前缀组合并将其保存回数据库。而且,在极少数需要重复 ID 的情况下,不要从 SEQUENCE 中获取数字,只需使用已有的数字即可。但是 SEQUENCE 将解决您创建“井”的问题,您可以在需要时从中抽取一个有保证的唯一编号

于 2010-10-14T13:34:33.990 回答
5

听起来您已将 3 条数据非规范化为 1 个字段。这是您的字符串字段当前包含的内容:

  • StringField char (12): STR-STR-####

这是您真正应该拥有的(仅示例字段名称;提供这些有意义的名称会有所帮助,但我不知道您的数据代表什么):

  • Str1 字符 (3): STR
  • Str2 字符 (3): STR
  • 身份证号码:####

您现在可以在 ID 字段中使用序列。

当您想取回原始字符串时,您可以连接 Str、Str2 和 ID 字段的内容(使用分隔连字符)。

简而言之:您的数据库设计已损坏,您现在正在为此付出代价。我的建议是通过将您的身份字段标准化为 3 个单独的字段来修复设计。或者,您会发现您花费数小时重新创建数据库的内置功能,最终得到的解决方案存在错误并且在竞争条件下存在可怕的问题。


或者,如果 PL/SQL 允许所有这些功能:

创建一个包含以下字段的表:

  • Str1 字符 (3)
  • Str2 字符 (3)
  • 当前 ID 整数

然后:

  • 对于 STR-STR 标识符的每个可能组合,在数据库中添加一个条目,并将“CurrentID”值设置为 0。
  • 编写一个用于检索下一个 ID 的存储过程。它将根据传入的STR-STR对锁定相关行,获取CurrentID中的值,增加值,解锁行并返回增加的值。
  • 每当您需要生成新 ID 时调用该过程。

您不会遇到并发问题,因为每次尝试获取 ID 都必须等待任何其他尝试完成。每个 STR-STR 对都有自己的计数器。

于 2010-10-14T14:30:19.237 回答
1

在该列上使用 UNIQUE INDEX 或 PRIMARY KEY!

于 2010-10-14T13:32:04.397 回答
1

现在有一些问题,在获取下一个数字和提交到数据库之间大约有 1000 行代码。问题是当两个人在几秒钟内创建一个具有相同标准的项目时,有时会发出相同的数字。

这令人担忧。您真的应该为此使用存储过程并将其全部包装在事务中。您无法保证任何两条记录都有不同的编号并会导致问题。但我同意 - 您需要将列设置为唯一标识符并使​​用主键 - 我假设它是一个关系数据库!

于 2010-10-14T13:37:26.993 回答
1

您可能需要某种序列化过程来完成此操作。我推荐的一种方法是在初始插入期间将此字段留空,等待过程提交,然后让另一个进程(例如重复作业)填充此列。

这个其他过程将根据您的业务规则填充所有行。这项工作将是唯一能够接触此列的工作,因此您将避免任何并发问题。

您可以将作业设置为每 X 秒运行一次,这意味着在此设置中该列将为空的一小段延迟。或者,您可以让初始会话在提交后通过某种序列化启动更新过程(对一行进行抢先锁定,这样两个更新过程就不能同时运行)。

于 2010-10-14T14:08:59.827 回答
1

这可以使用基于函数的唯一索引来完成。首先,您需要向表中添加一列,以指定标识符是否需要对每一行都是唯一的:

alter table ... add (identifier_must_be_unique varchar2(1)
    check (identifier_must_be_unique='Y'));

现在创建一个唯一的 FBI,它只保存需要唯一的标识符:

create unique index xxx on yyy 
(case when identifier_must_be_unique='Y' then identifier end);

最后,在您的标识符生成逻辑中,每当您需要标识符唯一时,设置 identifier_must_be_unique='Y'; 否则将其保留为空)。然后,FBI 将实施条件约束。

于 2010-10-14T14:51:17.623 回答
1

我几乎不想提出这个建议,但是由于您选择的方法非常糟糕,因此我将在这里展示的方法与之相比相当不错。原谅我的讽刺,但是当你有这么多用户告诉你你的方法总体上是错误的时,你应该考虑到这一点。

您可能正在某个地方运行这样的 SQL:

SELECT MAX(DENORMALIZED_FIELD)
INTO   BADLY_NAMED_VARIABLE
FROM   POORLY_ORGANIZED_TABLE
WHERE  DENORMALIZED_FIELD LIKE 'ABC-XYZ%';

然后稍后您可能会使用 拆分变量SUBSTR,将第二部分解析为 a NUMBER,将其递增,并使用新代码构建一个新变量。

您可以做的是将FOR UPDATE子句添加到SELECT语句中,对有问题的记录加锁。当然,您实际上并没有更新它们,但根据您的定义,您必须序列化操作。这是一种性能不佳、不可扩展且肮脏的方式来获得您想要的东西,但它应该可以工作。检查 Oracle 文档以查看所有含义。

于 2010-10-20T02:16:53.687 回答
0

创建一个 PRIMARY KEY 列或一个自动递增的 UNIQUE KEY 列(使用AUTOINCREMENT关键字或通过 SEQUENCE(如 Oracle 所做的那样))。这样,如果 2 个人同时添加 2 个确切的数据行,数据库将添加 2 个确切的值,但每个值都有自己的唯一 ID。

于 2010-10-14T13:38:13.887 回答
0

Yuor 代码绝对应该替换为 SEQUENCE。Oracle 很好地管理了这种并发性。

此外,此序列应保存在限制为 UNIQUE 的列中。

于 2010-10-14T13:40:27.063 回答
0

您可以添加一个表来保存所有已使用且需要唯一的标识符,如下所示:

create table unique_identifiers (id varchar2(12) primary key);

然后,每当在 350 行逻辑中生成需要唯一的 'STR-STR=####' 值时,将其插入该表中。如果插入失败,请获取另一个号码并重试。

于 2010-10-14T14:45:29.040 回答
0

你有一个损坏的数据模型。但是,我假设修复它不是一个可行的选择。如果你最大的问题是当你对包含序列值的表运行两个快速选择时重复键(你应该在这里使用序列......但你知道)......那么你可以尝试使用“select .. . for update”,它将锁定访问它的会话的行。请注意,此逻辑可能会在应用程序中引入延迟。

于 2010-10-14T16:28:05.010 回答
0

我可能遗漏了一些明显的东西,但我会建议类似于其他人之前建议的东西。在列上创建一个唯一约束,这样当您尝试保留记录时,如果其他进程已经使用了该自定义生成的 ID,它将失败。然后,您可以在 PL/SQL 中捕获该异常并实施某种重试逻辑,直到您能够成功地将生成的唯一 ID 插入到数据库中。

于 2010-10-14T16:59:01.770 回答
0

好吧,没有一个答案真正解决了问题,主要是因为我没有正确传达整个情况..

但基本上我在提交之前将我的检查放入一个循环中并在同一个循环中更新和提交并再次检查..如果仍然存在,循环再次运行,在这种情况下检查发生了..有点难以解释但是重复的机会极低(循环限制为 100)

于 2010-10-14T20:05:36.673 回答