如何在C#中生成随机唯一数字

5
public int GenPurchaseOrderNum()
{
    Random random = new Random();
    _uniqueNum = random.Next(13287, 21439);
    return UniqueNum;
}

我从数据库中删除了PONumber列的唯一约束,因为只有在成交时员工才应该生成采购单号。否则,采购单号将为0。

P.O.编号曾经有唯一约束,这迫使员工在所有情况下都要生成采购单号,以免数据库抛出唯一约束错误。

由于我删除了唯一约束,任何没有采购单号的报价都将携带0值。否则,将为P.O. #生成唯一值。但是,我在数据库中没有唯一约束,这使得我很难知道应用程序生成的P.O. #是否唯一。

我该怎么做?

希望我的问题足够清楚明白。


5
为什么数字需要随机? - Steve Severance
3
是的 - 为什么要随机?在我看来,您想要一个唯一的采购订单号,而不是一个随机的。如果是这样,那么可以很容易地使用带有Id列唯一约束的PO表来生成采购订单号。 - Cheeso
2
这些数字几乎总是连续的,不是吗?如果您需要一些其他不能被意外输入的唯一参考,则可以使用Guid。 - Thomas Kjørnes
我会从数据库中最后一个采购订单号加1,取得一个连续的数字。 - Tim Schmelter
只是猜测-您不想使用顺序号是因为它会透露出您生成的订单数量吗? - Mark Ransom
11个回答

11

每次使用 new Random() 都会初始化一个新实例。这意味着在一个紧密的循环中你会得到很多次相同的值。你应该只保留一个 Random 实例,并一直使用它的 Next 方法。

//Function to get random number
private static readonly Random getrandom = new Random();
private static readonly object syncLock = new object();
public static int GetRandomNumber(int min, int max)
{
    lock(syncLock) { // synchronize
        return getrandom .Next(min, max);
    }
}

11

GUID是一种开销较高的128位标识符。具体来说,你需要一个可读性强的PO#,使得使用GUID不可行。我更倾向于使用以下方案:

  1. 移除字段上的任何NOT NULL约束。
  2. 编写一个存储过程,在创建新的PO时将PO#字段设为NULL。在所描述的情况下,Null是最适合的,因为在数据库中NULL表示“未知”,由于您实际上没有PO#,所以它确实是未知的。
  3. 使用另一个存储过程,当交易完成时更新字段以递增到下一个可用的PO号码。这将在服务器端发生,因此无论更新来自哪个客户端,它仍将是该表中唯一的。然后,此存储过程可以将更新的结果集(如果需要)返回给客户端,以便他们可以看到新的PO#。

以上是概述。


对我来说似乎没问题,我以前用过这种模式,但是我对生成新的Guid并锁定表格的开销有些疑虑。 - Diego Pereyra
另一个选项是使用自动递增字段,并有另一个布尔字段指示交易是否已设置。假设“当交易被设置时,员工应该只生成P.O.#”不是硬性约束条件,那就可以这样做。 - BlueRaja - Danny Pflughoeft
迭戈,存储过程不会锁定整个表。它只会生成记录级锁,尽管丹尼可能有一个关于何时生成PO#的硬性要求的观点。 - Steve Brouillard

5
  • 不要使用0作为未知值。添加唯一约束并使用NULL表示未知/未生成的值。如果您愿意,可以将NULL显示为0给用户,但这与问题无关。
  • 不要使用随机数生成唯一数字,然后尝试插入,这会通过重试和重新生成使逻辑复杂化。使用序列生成器,如UPDATE seqnces SET sequence = sequence+1 OUTPUT INSERTED.sequence WHERE key = 'somekey'
  • 不要使用随机数生成业务含义值,如采购订单号。像上面那样使用序列生成器。

更新

使用SQL Server 2008及以上版本,您可以拥有一个唯一的过滤索引来作为不等于0的所有内容的约束:

create unique index idxTbaleUniquePoNumber 
 on <table> (po_number) 
 where (po_number > 0);

就像是既能吃蛋糕又能保留它一样...

请勿将0用于未知值 - 问题是应用程序会将0插入到PONumber列中。在应用程序级别上,我有一个“int poNum;”变量,它从PONumber文本框中获取值,如果文本框没有任何数字,则poNUm将传递0到数据库,即使我不想要这样。我将使用增量。 - peace

5
如果您真的需要一个随机值,而不是返回一个int,您可以使用GUID。这些保证是唯一的:

GUID是一个128位整数(16字节),可用于所有计算机和网络,无论何时需要唯一标识符。这样的标识符极少会重复。

来源

另一种方法是记录上次使用的数字,并在每次需要新的PO时简单地递增它。确保在其周围放置锁定,以便两个进程不会同时尝试递增它。

針對採購訂單,是否需要使用GUID?為什麼? - BlueRaja - Danny Pflughoeft
@BlueRaja - OP要求保证唯一性,我提供了两种解决方案。 - ChrisF

2
如果您想获得一个在范围内(比如13287到21439)连续的独特随机数序列,我会先创建一个包括所有有效整数的列表,然后随机交换它们。这相当于一种无序操作。这样就可以得到一个随机和独特的数字列表。
关键是要保存这个列表,因为如果您真的需要随机,生成时间可能会很长。然后,我会将其放入表格中,这样您就可以从任何地方索引它。

查找洗牌算法以生成列表。 - Mark Ransom

0
在这种情况下,当由于记录所代表的实体状态而导致某个字段或一组字段没有值时,我有时会喜欢将表格分成两个单独的表格,它们之间存在1对1的关系。
假设您的原始表格名为PurchaseOrder,并且具有类似以下结构的结构:
table PurchaseOrder
(
    PurchaseOrderID int identity not null,
    -- some other fields that are core to a purchase order
    -- ...
    PONumber int not null,
    -- some other fields that are related to a purchase order when the deal is set
    -- ...

    -- define primary key on PuchaseOrderID
    -- define unique constraint on PONumber field
)

我的建议是将该表格拆分,使其看起来像这样:

table PurchaseOrder
(
    PurchaseOrderID int identity not null,
    -- some other fields that are core to a purchase order
    -- ...

    -- define primary key on PuchaseOrderID
)


table PurchaseOrderDealInfo
(
    PurchaseOrderID int not null,
    PONumber int identity not null,
    -- some other fields that are related to a purchase order when the deal is set
    -- ...

    -- define primary key on PurchaseOrderID
    -- define foreign key on PurchaseOrderID related to PurcahseOrder.PurchaseOrderID
    -- define unique constraint on PONumber field
)

现在,您的特定于交易的信息已经在一个单独的表中。任何尚未设置交易的采购订单将不会在PurchaseOrderDealInfo表中有相应的记录。是的,如果您想检索与特定采购订单相关的所有信息,则需要将这些表连接在一起。但是,您可以获得一些好处:

  1. 您仍然可以对PONumber列应用唯一约束。

  2. 您可以利用SQL Server的标识功能为您生成PONumber。

  3. 如果您的表中有任何其他特定于交易的列,只是因为它们在设置交易之前没有值而使其可为空,则一旦将这些列移动到PurchaseOrderDealInfo表中,您可以设置为不允许为空(如果适用)。

  4. 如果您只想检索具有交易的采购订单,则可以简单地查询PurcahseOrderDealInfo表,而无需指定任何过滤条件。当然,如果您还需要来自PurchaseOrder表的信息,则需要进行内部连接。


0
从你写的内容来看,似乎你走错了方向:你把随机性和唯一性混淆了。
你已经移除了特定领域的约束,以便符合你所编写的代码。如果PONumber的值除了唯一性之外没有其他相关性,请让数据库为你生成它,或者生成一个GUID(如果数据库无法生成),这可以保证生成的值是唯一的。
否则,你需要自己做所有的艰苦工作。 你必须生成一个唯一的值,同时考虑到所有的线程和事务问题。

0

像之前的回答者提到的,我会建议使用唯一值而不是随机值。Remus建议的序列生成器可能是最好的方法。

然而,如果你真的非常需要一个随机值,我会推荐使用RNGCryptoServiceProvider而不是Random类,因为它更加随机。请访问MSDN获取更多信息。还有一个很好的StackOverflow上关于RNGCryptoServiceProvider的问题


0

我认为你一开始就走错了方向,现在情况变得更糟了。你没有提供任何真正的商业理由或要求来说明你的问题,这样很难看出我们的答案如何能够满足你的最终目标。

我认为你应该退后一步,清楚地制定你的目标,不考虑当前的物理实现。

通常,订单系统确实需要唯一的PO,因此您需要对其进行唯一约束。通常,系统不会以相同的方式存储PO和"pre-PO"。 "pre-POs"通常被称为报价或类似的东西,并且它们通常有自己的编号系统。因此,通常没有将NULL或0 PO编号与实际PO存储在一起的冲突,因为没有任何不是真正PO的东西存储在真正PO中。即使在想要将它们存储在一起的系统中,它们也只是按相同顺序分配一个数字。

此外,使用identity生成PO编号是完全可以的。


0

伪随机数怎么样?虽然几乎每个语言库中的随机数实现都是伪随机的,但您可以实现一些算法,使其具有随机性的外观,同时保持唯一性。

请参阅伪随机数生成器列表。我喜欢使用线性反馈移位寄存器来生成非连续的数字,并具有定义的周期(即,在重复数字之间调用的次数)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接