简体   繁体   中英

How Can I Generate Random Unqiue Numbers in C#

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

I removed unique constraint from the PONumber column in the db because an employee should only generate PO # when the deal is set. Otherwise, PO # would have 0.

PO Number used to have unique constraint, this forces employee to generate PO in all cases so the db doesn't throw unique constraint error.

Since i removed the unique constraint, any quote doesn't have PO will carry 0 value. Otherwise, a unique value is generated for PO #. However, i don't have a unique constraint in db which makes it hard for me to know whether the application generated PO # is unique or not.

What should i do?

I hope my question is clear enough

A GUID is a bit high in the way of overhead. Specifically, it sounds like you need a human readable number for the PO#, which makes a GUID impractical. I'd be more inclined to use the following scenario.

  1. Remove any NOT NULL constraints you have on the field.
  2. Have a stored procedure that you use to create a new PO that leaves the PO # field NULL. Null is most appropriate in the case described since in the world of DB NULL means "unknown" and since you don't actually HAVE a PO #, it IS unknown.
  3. Use a stored procedure that updates the field when the deal is complete to increment to the next available PO number. This will happen server side, so it doesn't matter what client the update comes from it will still be unique to that table. This stored procedure can then return the updated result set (if required) to the client so they can see the new PO #.

That's the 20k foot view.

Every time you do new Random() it is initialized . This means that in a tight loop you get the same value lots of times. You should keep a single Random instance and keep using Next on the same instance.

//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);
    }
}

If you really want a random value, rather than return an int you could use a GUID. These are guaranteed to be unique:

A GUID is a 128-bit integer (16 bytes) that can be used across all computers and networks wherever a unique identifier is required. Such an identifier has a very low probability of being duplicated.

Source

The alternative is to keep a record of the last number used and simply increment it every time you need a new PO. Make sure you put a lock around it so two processes don't try and increment it at the same time.

  • don't use 0 for unknown values. Add back the unique constraint and use NULL to represent unknown/not-generated values. If you wish, display NULL as 0 to the user, but that is orthogonal to the issue.
  • Don't use random to generate unique number and then try to insert, you'll complicate the logic unnecessarily with retries and regenerates. Use sequence generator like UPDATE seqnces SET sequence = sequence+1 OUTPUT INSERTED.sequence WHERE key = 'somekey'
  • Don't use random to generate business meaning values like purchase order numbers. Use a sequence generator, like above.

Updated

With SQL Server 2008 and above you can have a unique filtered index to act as a constraint for everything not equal to 0:

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

Sort of like eating the cake and having it too...

If you really want a series of unique random numbers in a range (say 13287 to 21439) I would create a list of all the valid integers and then randomly swap them around. Kind of a unsort if you like. This will give you a list of random, unique numbers.

The trick is to save that list since if you really wanted random it would take some time to generate. I would then put that in a table so you could index through it from wherever you need it.

From what you've wrote, it seems like you're heading the wrong way: You're confusing randomness with uniqueness.

You've removed a domain specific constraint, so that it will comply with the code you've wrote. If the value of the PONumber has no other relevance other than being unique, have the database generate it for you, or generate a GUID (if the database can't), which gives you some assurance the generated value will be unique.

Otherwise, it's all up to you to do the hard work. You'll have to generate a unique value, keeping in mind all threading and transactional issues.

Like the previous posters, I would recommend a unique value over a random value. The sequence generator Remus suggests is likely the best approach.

However, if you really really need a random value, I would recommend using RNGCryptoServiceProvider over the Random class because it's more random. Check out MSDN for more information . There's a good StackOverflow question on RNGCryptoServiceProvider as well.

I think you have gone down the wrong path to start with and it's getting worse. There is no real business justification or requirements accompanying your stated problem, and thus it is difficult to see how our answers can satisfy your end goals.

I think you should step back and clearly formulate your goals without respect to the curent physical implementation.

Typically, order systems do require unique PO, so you need to have a unique constraint on that. Typically, systems do not store POs and "pre-POs" in the same way. The "pre-POs" are usually called quotes or something similar, and they often have their own numbering system. Thus there usually is not a conflict in having NULL or 0 PO numbers, because there is not anything which is not a real PO stored with the real POs. Even in systems that want to store them together, they just go ahead and give them a number in the same sequence.

Also, an identity is perfectly fine to generate PO numbers.

How about pseudorandom numbers? Granted, nearly every implementation of random numbers in language libraries are pseudorandom already, but there are algorithms you can implement that will give an appearance of randomness while maintaining uniqueness.

See the list of pseudorandom number generators for some. I like the idea of the linear feedback shift register to generate numbers that are nonsequential and have a defined period (ie, number of invocations between repeating numbers).

In situations like this, where there is a field or a set of fields that simply don't have a value because of the state of the entity that a record represents, I sometimes like to break the table into two separate tables that are related in a 1-to-1 relationship.

Let's suppose that your original table is called PurchaseOrder and has a structure that looks something like this:

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
)

My suggestion would be to break that table apart so that it looks something like this:

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
)

Now your deal-specific information is in a separate table. Any purchase orders that don't yet have a deal set will simply not have a corresponding record in the PurchaseOrderDealInfo table. Yes, you will need to join the tables together if you want to retrieve all of the information pertaining to a specific purchase order. However, you get some benefits:

  1. You can still apply a unique constraint to the PONumber column.

  2. You can take advantage of SQL Server's identity feature to generate your PONumber for you

  3. If you had any other deal-specific columns in your table that you made nullable simply because they wouldn't have a value until a deal was set, then once you move those columns into the PurchaseOrderDealInfo table you can set to not allow nulls (if appropriate).

  4. If you only want to retrieve purchase orders that have deals, then you can simply query the PurcahseOrderDealInfo table without having to specify any filter criteria. Of course, if you also need information from the PurchaseOrder table then you will need to do an inner join.

Your autonumber PK in the database can be used as a unique PO number... just read and copy it when it becomes a "real" non-zero PO number.

This won't be "random" but it will be unique.

Now for the nerd answer: It's not possible for your computer to generate random numbers without some chaotic "random" seeds

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM