【问题标题】:How Can I Generate Random Unqiue Numbers in C#如何在 C# 中生成随机唯一数
【发布时间】:2010-06-17 19:20:15
【问题描述】:
public int GenPurchaseOrderNum()
{
    Random random = new Random();
    _uniqueNum = random.Next(13287, 21439);
    return UniqueNum;
}

我从数据库中的 PONumber 列中删除了唯一约束,因为员工 应该只生成 P.O. # 当交易成立时。否则,P.O. # 会有 0。

邮政信箱Number 曾经有唯一的约束,这迫使员工生成 P.O.在所有情况下,数据库都不会抛出唯一约束错误。

由于我删除了唯一约束,任何报价都没有 P.O.将携带 0 值。否则,会为 P.O. 生成唯一值。 #.但是,我在 db 中没有唯一约束,这让我很难知道应用程序是否生成了 P.O. # 是否唯一。

我该怎么办?

我希望我的问题足够清楚

【问题讨论】:

  • 为什么要随机数?
  • 是的 - 为什么是随机的?在我看来,您想要一个唯一的 PO 编号,而不是随机编号。如果这是真的,那么应该很容易使用对 Id 列具有唯一约束的 PO 表来生成 PO 编号。
  • 这些数字几乎总是连续的,不是吗?如果您需要一些其他无法意外输入的唯一参考,我会使用 Guid。
  • 我会取一个连续的数字,你从最后一个 P.O. 开始增加 +1。 # 在数据库中。
  • 只是一个猜测 - 您不想使用序列号,因为它会显示您正在生成多少订单?

标签: c# .net sql-server


【解决方案1】:

GUID 的开销有点高。具体来说,听起来您需要一个人类可读的 PO# 编号,这使得 GUID 不切实际。我更倾向于使用以下场景。

  1. 删除字段上的任何 NOT NULL 约束。
  2. 有一个用于创建新 PO 的存储过程,它使 PO # 字段为 NULL。 Null 最适合所描述的情况,因为在 DB 的世界中,NULL 意味着“未知”,并且由于您实际上没有 PO #,因此它是未知的。
  3. 使用存储过程在交易完成时更新字段以递增到下一个可用的采购订单号。这将发生在服务器端,因此更新来自哪个客户端并不重要,它仍然对该表是唯一的。然后,此存储过程可以将更新的结果集(如果需要)返回给客户端,以便他们可以看到新的 PO #。

这是 20k 英尺的视图。

【讨论】:

  • 对我来说似乎是正确的,我以前使用过这种模式,但我对针对表上的锁生成新 Guid 的开销有些怀疑。
  • 另一种选择是使用自动递增字段,并使用另一个布尔字段指示是否设置了交易。假设 an employee should only generate P.O. # when the deal is set 不是 hard 约束,即..
  • 迭戈,存储过程不会锁定整个表。它只会生成一个记录级别的锁,尽管 Danny 可能对何时生成 PO# 有一定的硬性要求。
【解决方案2】:

每次你执行 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);
    }
}

【讨论】:

    【解决方案3】:

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

    GUID 是一个 128 位整数(16 个字节),可以在需要唯一标识符的所有计算机和网络中使用。这样的标识符被复制的概率非常低。

    Source

    另一种方法是记录最后使用的编号,并在每次需要新采购订单时简单地增加它。确保你在它周围加了一个锁,这样两个进程就不会尝试同时增加它。

    【讨论】:

    • 采购订单的 GUID #?为什么?
    • @BlueRaja - OP 要求保证唯一性,我提供了两种解决方案。
    【解决方案4】:
    • 不要将 0 用于未知值。添加唯一约束并使用 NULL 来表示未知/未生成的值。如果您愿意,可以向用户显示 NULL 为 0,但这与问题无关。
    • 不要使用随机生成唯一编号然后尝试插入,因为重试和重新生成会使逻辑不必要地复杂化。使用像UPDATE seqnces SET sequence = sequence+1 OUTPUT INSERTED.sequence WHERE key = 'somekey'这样的序列生成器
    • 不要使用随机来生成业务含义值,例如采购订单号。使用序列生成器,如上所示。

    更新

    使用 SQL Server 2008 及更高版本,您可以拥有一个唯一的 filtered index 来作为所有不等于 0 的约束:

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

    有点像吃蛋糕也吃...

    【讨论】:

    • "# don't use 0 for unknown values" - 问题是 0 从应用程序插入到 PONumber 列。在应用程序级别我有“int poNum;”从 PONumber 文本框中获取值的变量,如果文本框没有任何数字 poNUm 将传递 0 到 db 即使我不想那样。我会跟着增量走
    【解决方案5】:

    如果你真的想要一个范围内的一系列唯一随机数(比如 13287 到 21439),我会创建一个所有有效整数的列表,然后随机交换它们。如果你喜欢的话,有点乱。这将为您提供一个随机的、唯一的数字列表。

    诀窍是保存该列表,因为如果您真的想要随机生成,则需要一些时间。然后我会把它放在一个表中,这样你就可以从任何你需要的地方索引它。

    【讨论】:

    • 查找改组算法以生成列表。
    【解决方案6】:

    从您所写的内容来看,您似乎走错了路:您将随机性与唯一性混为一谈。

    您已删除域特定约束,以便它符合您编写的代码。如果 PONumber 的值除了唯一性之外没有其他相关性,让数据库为您生成它,或者生成一个 GUID(如果数据库不能),这可以确保生成的值是唯一的。

    否则,这一切都取决于您是否努力工作。 您必须生成一个独特的值,牢记所有线程和事务问题。

    【讨论】:

      【解决方案7】:

      与之前的海报一样,我建议使用唯一值而不是随机值。 Remus 建议的序列生成器可能是最好的方法。

      然而,如果你真的真的需要一个随机值,我会推荐使用 RNGCryptoServiceProvider 而不是 Random 类,因为它更随机。查看 MSDN 以获取 more information。还有一个很好的 StackOverflow question on RNGCryptoServiceProvider

      【讨论】:

        【解决方案8】:

        我认为你一开始就走错了路,而且情况越来越糟。您所陈述的问题并没有真正的商业理由或要求,因此很难看出我们的答案如何能够满足您的最终目标。

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

        通常,订单系统确实需要唯一的采购订单,因此您需要对此有唯一的约束。通常,系统不会以相同的方式存储 PO 和“pre-PO”。 “pre-POs”通常被称为引号或类似的东西,它们通常有自己的编号系统。因此,拥有 NULL 或 0 PO 编号通常不会发生冲突,因为没有任何不是真实 PO 的东西与真实 PO 一起存储。即使在想要将它们存储在一起的系统中,它们也会继续按照相同的顺序给它们一个数字。

        此外,生成 PO 编号也可以使用身份。

        【讨论】:

          【解决方案9】:

          伪随机数怎么样?诚然,语言库中几乎所有随机数的实现都已经是伪随机的,但是您可以实现一些算法,在保持唯一性的同时呈现随机性。

          请参阅list of pseudorandom number generators 了解一些信息。我喜欢 linear feedback shift register 的想法,它可以生成非连续的数字并且有一个定义的周期(即重复数字之间的调用次数)。

          【讨论】:

            【解决方案10】:

            在这种情况下,由于记录所代表的实体的状态,有一个字段或一组字段根本没有值,我有时喜欢将表分成两个单独的表是一对一的关系。

            假设您的原始表名为 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 表中的信息,则需要进行内部连接。

            【讨论】:

              【解决方案11】:

              您在数据库中的自动编号 PK 可以用作唯一的采购订单号...当它变成“真正的”非零采购订单号时,只需读取并复制它。

              这不是“随机的”,而是独一无二的。

              现在是书呆子的答案: 没有一些混乱的“随机”种子,您的计算机不可能生成随机数

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2018-02-24
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多