避免在数据库中重复标识符

时间:2010-10-14 13:30:36

标签: sql oracle plsql duplicates no-duplicates

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

当用户创建项目时,基于某个creteria,我们给项目一定数量。在save函数中,调用一个函数来准备第一个初始字符串,然后保存数字的表是扫描并分配该特定字符串的下一个可用4位数字。

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

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

即使此代码减少了重复的可能性,如果我同时保存2个项目,我仍然会得到重复的数字..

任何人都知道如何避免重复并将重复数字的可能性降至0?

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

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

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

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

13 个答案:

答案 0 :(得分:5)

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

[响应编辑到问题中的新要求]

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

答案 1 :(得分:5)

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

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

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

  • Str1 char(3):STR
  • Str2 char(3):STR
  • ID int:####

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

如果要恢复原始字符串,可以连接Str,Str2和ID字段的内容(用连字符分隔)。

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


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

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

  • Str1 char(3)
  • Str2 char(3)
  • CurrentID int

然后:

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

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

答案 2 :(得分:1)

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

答案 3 :(得分:1)

  

现在有一些问题,   大约有1000行代码   获得下一个数字之间的关系   提交到数据库。问题   当2个人创建一个项目时   几秒钟内相同的标准   彼此,有时是相同的数字   发行。

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

答案 4 :(得分:1)

您可能需要某种序列化过程才能完成此操作。我建议的一条路径是在初始插入期间将此字段留空,等待过程提交,然后让另一个进程(例如,定期作业)填充此列。

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

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

答案 5 :(得分: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';否则将其保留为null)。然后FBI将实现条件约束。

答案 6 :(得分:1)

我几乎不想暗示这一点,但是既然你确定的方法非常糟糕,那么我将在这里展示的那个相当不错。原谅这个讽刺,但当你有这么多用户告诉你你的方法一般是错的时候你应该考虑到这一点。

某处您可能正在运行这样的SQL:

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

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

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

答案 7 :(得分:0)

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

答案 8 :(得分:0)

Yuor代码绝对应该用SEQUENCE代替。 Oracle非常好地管理这种并发性。

此外,此序列应保存在一个约束为UNIQUE的列中。

答案 9 :(得分:0)

您可以添加一个表来保存已使用过的所有标识符,并且必须是唯一的,如下所示:

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

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

答案 10 :(得分:0)

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

答案 11 :(得分:0)

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

答案 12 :(得分:0)

答案中没有人能真正解决问题,主要是因为我没有正确地传达整个情况。

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