Unique Value in non-primary field - php

I'm generating a random 10 character string with php, and inserting it into a column in my DB. My issue is that I want this string to be unique(in the database). I've thought of many ways of doing this, but wondering which way is the most efficient.
My PHP looks like this(random string can only be 10 chars long):
//generates an almost unique(not quite) ticket number
$pretrimmedtask = md5(uniqid(mt_rand(),true));
$tasknum = substr($pretrimmedtask ,0,10);
I then take this "unique" value and insert it. But because of the trim of the string, this value is by no means unique. I'm wondering what is the best way of making sure this value could never be duplicated, while still being efficient.
(I understand that querying the db to look for this value in there is possible... but I would rather do it in a more elegant fashion)

You should update your table and make the relevant column be a UNIQUE KEY, than try to insert the generated string, if no rows where inserted, generate another key and try again.
ALTER TABLE table_name ADD UNIQUE KEY (column_name);
The code below will try to INSERT a new row into table1, if unable it will try again with a different random generated $key.
IE. the query will not succeed if col2 has a unique key constraint and the value of $key already exists in the column.
function generate_random_string () {
$charset = array_merge (
range ('a', 'z'), range ('A','Z'), range ('0','0')
);
shuffle ($charset);
return join ('', array_slice ($charset, 0, 9));
}
/* ....................................................... */
do {
$key = generate_random_string ();
} while (
!mysql_query ("INSERT INTO table1 (col1,col2) VALUES (123, '$key')")
);
You can of course use your own algorithm for generating random strings.
NOTE: Make sure that the query can potentially succeed so that you don't get caught in an endless loop.

Create unique mysql index that covers only that field and insert the value in a loop until success.
Like:
while (true) {
$random = generate it;
try to insert;
if (inserted without errors) break;
}

Does it has to be 10 character. With crypt() you can generate 13 character long hashes.
Here http://www.php.net/manual/en/function.hash.php you can check the length of different hashing methods. None of them unfortunatelly produces exactly 10 character long string. But you can generate 8 character long string and add two characters.
Another possible solution that I came up with is using current date of generating the string. Unixtimestamp is only numbers and to long but we can convert date into 10 char string in the following manner
Create two arrays, first with keys from 1 til 31 and assign one character for each key (26 letters plus 10 numbers will do the trick), the second array need to have keys from 0 til 99 and have values of two charater long string.
Now take the day, month, year (2 digits), hour, minute and seconds of the current time and replace the value with the value from the array, where day and month take from the first array and the rest from the second. Combine that and you have 10 character long unique string.

Had this issue by myself.
I use (insert) time() and insert/row id (+ special letters array) md5-ed all together as one string and hashed - for some cookie purposes lets say. So, that key is exposed.
Insert (or row) id cannot be duplicated, and merged with unix timestamp (10 digits)+random letters and md5-ed all together creates surely unique "second key" somewhat harder to break what is available via cookies. In this case is impossible to break it.
But it is a hash.
If 10 chars is essential - as I can't find reason to be - you may create function for creating keys like (99999999999999999999-primary key)+substr 10 with letters included also, but that depends on a level of exposure of that key.
However, substr is not an option, and primary key role is simply - essential.

Related

Best way to create numeric hash/ID from a string?

I'm building a MySQL database with a table that will store lots of rows (say, like 1.000.000).
Each row will have a numeric ID but I don't want to make it incremental, instead it has to be generated from a unique string.
For example, a user ABC will create a new element at time 123, so the original string will be "ABC-123". A PHP function will "translate" it to a number.
This way, I'll have the possibility to re-generate the same ID from the same pair of data in future. More or less... see it as a Java hashCode() function.
I've found this function that "translates" a string into a number:
function hashCode($string) {
return base_convert(substr(md5($string), 0, 16), 16, 10);
}
I have some doubts about it. First, it starts from creating an md5 hash which is 32 characters long, then cuts it to 16. It's a visible lack of data so how could that be an unique hash?
Second, the produced 16-digits number is converted from base-16 to base-10, so the max value is 18446744073709552046. The MySQL column that will store this number has an UNSIGNED BIGINT datatype so the maximum value is 18446744073709551615. It's not enough since
18446744073709551615 - 18446744073709552046 = -431
Am I missing something, or is there a better way to do what I need?

PHP/MySQL - Convert database value to MD5, then shrink to a portion of that value?

I am using PHP and MySQL to create a string from a value and later compare it to a MD5 hash of the same value.
For instance, in MySQL i have a string value: somerandomvalue
In PHP I get that string value and transfer it to a local variable to hold the string value: $prdAlias
I transform the string value to a MD5 hash value:
$prdAlias = md5($prdAlias);
Then I take only the first 6 characters of that value for use later:
$prdAlias = mb_substr($prdAlias, 0, 6);
LATER
I have the first 6 characters of the MD5 value, I call it: $prdAlias
Now in MySQL i want to compare $prdAlias to the value that i started off with: somerandomvalue. To do that, I must convert the value in the database to a MD5 hash then take only the first 6 characters of the hash and compare that to $prdAlias
So I have a prepared statement:
if ($stmt = $link->prepare("
SELECT alias
FROM `products`
WHERE alias = ?
"))
{
... ETC
}
My question now is within this statement, how could i convert the alias value to MD5 and take only the first 6 characters of that to use in the WHERE clause?
Any assistance would be greatly appreciated, thank you!
EDIT: I am currently running a while loop and checking for the value by processing each row until a match is found... This is not ideal with thousands of rows.
You could use the mysql MD5() function to do this on the database server:
WHERE LEFT(MD5{alias), 6) = ?
but that would still require a full table scan, so would basically be identical to your while loop. If you want this to be fast, you need an index. I don’t think mysql has computed indexes that means you will have to add a column for the first six characters of the md5 of alias and compare against that.
I would personally store the whole hash and do a LIKE "123456%" though. Just a gut feeling that might be smarter in the long run. On the other hand if you only store the first six characters, you could add a unique key on that column and detect collisions early on.

Generate sequence on digits. Sequences should not be any similar

I'd like to generate a long list of 9-digits sequences.
Let's call them ID.
So each ID is unique and the main purpose is to have them all really different. It is unacceptable to have 2 IDs which differs by 1 or 2 digits in sequence.
Do you have any ideas how to implement it without comparing each new generated ID with each previously generated?
Probably there is some algorithm already or simple MYSQL function to compare how close those strings are?
You could try the following formula for your ID's - you would only need to check that the ID value doesn't already exist in the table (salt is a constant between 0 and 100 that doesn't ever change once you pick a value - I would recommend using a prime number, and definitely not 0):
ID = random integer * 101 + salt;
This generates ID values like the following (for salt = 73):
469956305
017775467
001195913
913620520
156482807
577463533
470183959
049290800
078643925
141526626
If you take any two of these ID values and compare them, you'll notice that no two numbers differ by only one or two digits in sequence. I wrote a script to compare all possible ID values between 0 and 3000000, and there were no two ID values of this form differing by 1 or 2 digits in sequence. If you want to test it out yourself, here's the script I used (in C#): http://ideone.com/lFHnlX - I reduced the upper limit because of timeout on IDEone.
You want to get away with not-checking for uniqueness and you don't want IDs to be similar? Then you're really looking for UUIDs/GUIDs.
MySQL's built-in uuid() function will get you there.
As Robert Harvey points out, UUIDs are alphanumeric (not numeric) and longer than 9 characters, but you're going to have to sacrifice something – you cannot satisfy all of your constraints simultaneously.

How to generate unique numeric value with fixed length from given data in PHP?

How to generate unique numeric value with fixed length from given data in PHP? For instance, I can have a string that contains numbers and characters and I need to generate unique numeric value with length 6. Thanks!
You won't be able to generate a unique numeric value out of an input with any algorithm. That's the problem of converting an input into a pseudorandom output. If you have an input string of 20 characters and an output of only 6, there will be repeated results, because:
input of 20 characters (assuming 58 alphanumerical possibilities):
58^20 = 1.8559226468222606056912232424512e+35 possibilities
output of 6 characters (assuming 10 numerical possibilities):
10^6 = 1000000 possibilities
So, to sum up, you won't be able to generate a unique number out of a string. Your best chances are to use a hashing function like md5 or sha1. They are alphanumerical but you can always convert them into numbers. However, once you crop them to, let's say, 6 digits, their chances to be repeated increase a lot.
It is impossible to generate a completely unique value given an arbitrary value with a limit on the number of characters unfortunately. There are an infinite number of possible values, while there are only 999999 possible values in a numeric value of length 6.
In PHP however you can do the following:
$list_of_numeric_values = array();
foreach ($list_of_given_values as $value)
{
if (!in_array($value, $list_of_numeric_values))
$list_of_numeric_values[] = $value;
}
After this is complete, the array then will have a unique key for each possible value you can use.
If you dont need to calculate these all at the same time you can follow a similar algorithm where instead of just "searching" the array using PHP perhaps its a SELECT on a MySQL table to see if the entry currently exists, and using the auto increment of the primary key to get your value.

Create a unique 4-byte Integer number from a String in PHP

I have a SQL table which uses strings for a key. I need to convert that string (max. 18 Characters) to a unique (!) 4-byte integer using PHP. Can anyone help?
Unique? Not possible, sorry.
Let's take a closer look:
With 18 characters, even if we were assuming only the 128 possible characters of ASCII (7 bits), you'd get 128^18 possible strings (and I'm not even going into the possibility of shorter strings!), which is about 8E37 ( 8 and 37 zeroes ).
With a 4-byte integer, you're getting 256^4 possible integers, which is about 4E9 ( 4 billion ).
So, you have about 4E28 more strings than you have integers; you can't have an unique mapping.
Therefore, you'll definitely run into a collision as soon as you enter the 4294967297th key, but it is possible to run into one as soon as you enter more than one.
See also: http://en.wikipedia.org/wiki/Pigeonhole_principle
Keep a lookup-table of strings to integers. Everytime you encounter a new string you add it to the mapping table and assign it a new unique ID. This will work for about 2^32 strings which is probably enough.
There is no way to do this for more that 2^32 distinct strings.
You can't. A four-byte integer can represent 2^32 = 4 billion values, which is not enough to hold your target space.
If you currently have less then 4 billion rows in the table, you could create a cross table that just assigns an incremental value to each. You'd be limited to 4 billion rows with this approach, but this may be fine for your situation.

Categories