13

I was wondering if anybody knew a good way to create a unique random integer id for a primary key for a table. I'm using MySQL. The value has to be integer.

Anders
  • 8,307
  • 9
  • 56
  • 88
MindGame
  • 1,211
  • 6
  • 29
  • 50
  • 3
    why not just `AUTO_INCREMENT` ? – RobertPitt Nov 19 '10 at 14:15
  • 3
    Because I want to use that value to Encode to Base62 and then use that for an id in a url. If i auto increment, it might be obvious to the user how the url id is generated. – MindGame Nov 19 '10 at 14:19
  • 4
    To get around your issue of security, you could use AUTO_INCREMENT and include a randomly created number as a field. To get your userid, you take the AUTO id + that random number. Run it through a hash (SHA128 will work just fine). Even if the random number is the same (which it realistically won't be), combining it with another number then SHA'ing it will give you your userid. – g19fanatic Nov 19 '10 at 15:04

8 Answers8

14

In response to: "Because I want to use that value to Encode to Base62 and then use that for an id in a url. If i auto increment, it might be obvious to the user how the url id is generated."

If security is your aim then using Base62, even with a "randomly" generated number won't help.

A better option would:

  • Do not re-invent the wheel -- use AUTO_INCREMENT
  • Then use a cryptographic hash function + a randomly generated string (hidden in the db for that particular url) to generate the final "unique id for that url"
Gary Green
  • 22,045
  • 6
  • 49
  • 75
  • 2
    Thank you for responding. Okay I use auto increment and then use a cryptographic hash function + a randomly generated string. But I need my url to be short. Short like how tinyurl does it. By using cryptographic won't I get a "unique id for that url" that is long? – MindGame Nov 19 '10 at 15:05
11

If your're open to suggestions and you can implement it, use UUIDs. MySQL's UUID() function will return a 36 chars value which can be used for ID.

If you want to use integer, still, I think you need to create a function getRandID() that you will use in the INSERT statement. This function needs to use random + check of existing ids to return one that is not used before.

Check RAND() function for MySQL.

Saravanan Sachi
  • 2,572
  • 5
  • 33
  • 42
byte_slave
  • 1,368
  • 1
  • 12
  • 24
  • 1
    How do you make sure the random value is not being used by another person at the same time. For example person A and B get the same random number. Both check at the same time if it exists, it doesn't so they both insert. Of course, one fails because of the primary key constraint. What do you do, to go around that? Locking tables? Never used it and kind of uneasy of using it. – MindGame Nov 19 '10 at 14:25
  • 1
    Thats the beauty of UUID (Universal unique identifier). Quoting Wikipedia: "The intent of UUIDs is to enable distributed systems to uniquely identify information without significant central coordination. Thus, anyone can create a UUID and use it to identify something with reasonable confidence that the identifier will never be unintentionally used by anyone for anything else. Information labeled with UUIDs can therefore be later combined into a single database without needing to resolve name conflicts." – byte_slave Nov 20 '10 at 16:29
  • 3
    @Tesh I would just repeat the function in the CATCH when an exception arose due to the UNIQUE constraint and continue until you are able to insert a row with that random Id. – Liron Harel Nov 27 '15 at 16:23
7

How you generate the unique_ids is a useful question - but you seem to be making a counter productive assumption about when you generate them!

My point is that you do not need to generate these unique id's at the time of creating your rows, because they are essentially independent of the data being inserted.

What I do is pre-generate unique id's for future use, that way I can take my own sweet time and absolutely guarantee they are unique, and there's no processing to be done at the time of the insert.

For example I have an orders table with order_id in it. This id is generated on the fly when the user enters the order, incrementally 1,2,3 etc forever. The user does not need to see this internal id.

Then I have another table - unique_ids with (order_id, unique_id). I have a routine that runs every night which pre-loads this table with enough unique_id rows to more than cover the orders that might be inserted in the next 24 hours. (If I ever get 10000 orders in one day I'll have a problem - but that would be a good problem to have!)

This approach guarantees uniqueness and takes any processing load away from the insert transaction and into the batch routine, where it does not affect the user.

Rob Beer
  • 199
  • 2
  • 2
3

You can use an AUTO_INCREMENT for your table, but give the users the encrypted version:

encrypted_id: SELECT HEX(AES_ENCRYPT(id, 'my-private-key'));

id: SELECT AES_DECRYPT(UNHEX(encrypted_id), 'my-private-key');

Sebastian Lenartowicz
  • 4,695
  • 4
  • 28
  • 39
Const Mi
  • 39
  • 3
2

How about this approach (PHP and MySQL):


Short

  1. Generate random number for user_id (UNIQUE)
  2. Insert row with generated number as user_id
  3. If inserted row count equal to 0, go to point 1

Looks heavy? Continue to read.


Long:

Table:

users (user_id int UNIQUE)

Code:

<?php
// values stored in configuration
$min = 1;
$max = 1000000;

$numberOfLoops = 0;
do {
    $randomNumber = rand($min, $max);

    // the very insert
    $insertedRows = insert_to_table(
        'INSERT INTO foo_table (user_id) VALUES (:number)', 
        array(
            ':number' => $randomNumber
        ));

    $numberOfLoops++;

    // the magic
    if (!isset($reported) && $numberOfLoops / 10 > 0.5) {
        /**
         * We can assume that at least 50% of numbers
         * are already in use, so increment values of
         * $min and $max in configuration.
         */
        report_this_fact();
        $reported = true;
} while ($insertedRows < 1);

  1. All values ($min, $max, 0.5) are just for explanation and they have no statistical meaning.
  2. Functions insert_to_table and report_this_fact are not build in PHP. The are also as numbers just for clarify of explanation purposes.
TheFrost
  • 1,265
  • 2
  • 15
  • 29
0

my way, for both 32bit and 64bit platform. result is 64bit

function hexstr2decstr($hexstr){
    $bigint = gmp_init($hexstr, 16);
    $bigint_string = gmp_strval($bigint);
    return $bigint_string;
}

function generate_64bitid(){
    return substr(md5(uniqid(rand(), true)), 16, 16);
}

function dbGetUniqueXXXId(){
    for($i = 0; $i < 10; $i++){
        $decstr = hexstr2decstr(generate_64bitid());

        //check duplicate for mysql.tablexxx
        if($dup == false){
            return $decstr;
        }
    }
    return false;
}
andrewchan2022
  • 4,953
  • 45
  • 48
0

There is an AUTO_INCREMENT feature. I would use that.

See here more examples.

galoget
  • 722
  • 9
  • 15
drew
  • 1,312
  • 8
  • 20
0

AUTO_INCREMENT is going to be your best bet for this.

Here are some examples.

If you need to you can adjust where the increment value starts (by default it's 1).

galoget
  • 722
  • 9
  • 15
Ben
  • 60,438
  • 111
  • 314
  • 488