i need to create 6 alphanumeric for primary key in a table , however i just think to make int field with autoincrement and get the max value of that field and process with this code so it can be alphanumeric and stored in different field.. does this idea and code meet the requirement? and is it good? will it always be unique?
<?php
$code = the max value retrieved from the autoincrement int
function getNextAlphaNumeric($code) {
$base_ten = base_convert($code,36,10);
$result = base_convert($base_ten+1,10,36);
$result = str_pad($result, 6, '0', STR_PAD_LEFT);
$result = strtoupper($result);
return $result;
}
Here's something to think about.
Create 6 character primary key.
Generate code with php
Insert it using INSERT IGNORE; if no rows affected (not very likely in the beginning), try again.
Done. A unique code was added.
Related
I need to generate unique custom reference ids for each user based on their user id. Right now I'm using the md5 method for this and have limited the length to 12 digits/characters.
$user_id = '120';
$ref_id = substr(md5($user_id, 0, 12);
I know there are many ways to generate a string from another string, but what would be the best way to generate a simple but unique user ID with a relatively short length (max. 16 chr/digits)?
The ID is used to preserve and mask the true user ID or the name of the user in publications.
why not just use their ID number?
Because user ids do not have the same scheme and are sequential. I want all of my users to have let’s say a 16 chr/digit but unique ref ID. It's not really about security, but here the uniqueness is in the foreground. I just selected MD5 for the staging server. I am open for any other advice.
From the conversation in comments, a good solution would be to generate a unique random key and associate it with the value (such as the database row).
This random key is neither an encryption or a hash. This key is a unique reference variable.
So for each user you have their database membership row, and one column would be "reference_key"; this can be populated when a unique value associated only with this account,
For Example
The below code will generate a unique key and save it to the array value $saver['reference_key'] , you can then insert this into your database when you save your other customer data.
The MySQL column should be UNIQUE indexed and UTF8mb4 collation and character set.
The reason that the testing checks if the value exists already would be its easier to re roll the value before the MySQL error is triggered when an identical value is tried to be inserted into the UNIQUE column.
function nonce_generator($length = 40)
{
// Source can be any valid characters you want to use.
$source = 'abcdefghijklmnopqrstuvwxyzANCEDFGHIJKLMNOPQRSTUVWXYZ0123456789!-=+';
$max = strlen($source) - 1;
$i = 0;
$output = "";
do {
$output .= $source[random_int(0,$max)];
$i++;
}
while($i < $length);
return $output;
}
...
do {
$found = false;
$saver['reference_key'] = nonce_generator(12); //see function above to generate a key.
$check = $dataBase->getSelect("SELECT COUNT(*) as numb FROM customer WHERE reference_key = ? ", $saver['reference_key']);
if ($check['numb'] > 0) {
// check if key already exists in the database.
$found = true;
}
unset($check);
} while ($found === true);
// once a unique key has been found then save this to the database user.
// along with all other user details.
$dataBase->arrayToSQLInsert("customer", $saver);
Please note that for this code uses customised database interactions and is for illustration purposes ONLY
Advantages:
Unique key does not reference any other customer data.
Unique key is not a hash or encryption so can not be 'compromised'.
Key is assured to be unique.
Disadvantages:
On very large data sets the do/while process of finding a unique value may cause a slight slowdown
I have a table which stores the ID of support cases using the primary key (column name = caseid).
I have now got to about 100,000 caseid and the number is just too big. I wish to somehow start from a lower number such as 1000.
How do I achieve something like this by not having to delete/archive existing records and not having to change the unique caseid's to another column (keep it as the primary key column)
To reset Primary Key, you can follow below steps:
Create temporary table with structure same as main table. Let's say table name is tbl_cases
CREATE TABLE tbl_cases_tmp LIKE tbl_cases;
ALTER TABLE tbl_cases_tmp ADD old_caseid int NOT NULL DEFAULT '0';
DUMP all data from tbl_cases to tbl_cases_tmp. caseid will be stored in old_caseid column.
INSERT INTO tbl_cases_tmp (name, summary, old_caseid)
SELECT name, summary, caseid FROM tbl_cases;
For any other tables having references to tbl_cases. Let's say tbl_reference
UPDATE tbl_reference tr
JOIN tbl_cases_tmp tc
ON tr.caseid = tc.old_caseid
SET tr.caseid = tc.caseid;
Before using Steps 4 and 5, ensure your tables tbl_cases_tmp and all references are properly updated.
Drop tbl_cases
DROP table tbl_cases;
Rename tbl_cases_tmp to tbl_cases
RENAME TABLE tbl_cases_tmp TO tbl_cases;
What about find lowest id and subtract its Value from all ids?
Then you'll be able to re-set the id to a lower number
Edit:
This suppose that there are unused ids and no recods related to them
In case your problem is with conveying the caseid e.g. from the customer via phone to the help desk you might consider leaving the actual case id as-is but change the alphabet/set of digits when showing it.
E.g. switch from decimal to hexadecimal and you've increased the range of values that can be displayed as four digits/characters from 9999 to 65535 (hex:ffff).
Now consider a different set of digits like 3479ACEFHJKLMNPRTUVWXY* and the range of numbers that can be displayed using only four digits/characters increases quite a lot.
<?php
echo getCode(234255), "\r\n"; // up until "here": four digits
echo getCode(234256), "\r\n"; // ok, now it's five
echo getCode(5100000), "\r\n"; // but stays five until > 5 millions
function toBase(/* positiv integer*/ $n, array $alphabet) {
$retval = '';
do {
$retval = $alphabet[ $n%count($alphabet) ] . $retval;
$n = intval( $n / count($alphabet) );
}
while( ($n=intval($n)) > 0);
return $retval;
}
function getCode(/*int*/ $caseid) {
static $alphabet = ['3','4','7','9','A','C','E','F','H','J','K','L','M','N','P','R','T','U','V','W','X','Y'];
return toBase($caseid, $alphabet);
}
prints
YYYY
43333
YTYAA
*) an alphabet containing only unambiguous characters.
Hello so first of all please consider this question as a newbie one because I can just set an ID field and add zerofill so it would look like 000001, 000002 and so fort. But what I did is wrong, and the system is already big so please consider my question. I have a table named accounts which has an id field and sponsorID field. Now what I did looks like this (btw I am using slim framework):
$db = new db();
$sponsorIDrandom = mt_rand(100000, 999999);
$bindCheck = array(
":sponsorID" => $sponsorIDrandom
);
$sponsorIDChecker = $db->select("accounts", "sponsorID = :sponsorID", $bindCheck);
$generate_SID = null;
do {
$generate_SID = mt_rand(100000, 999999);
} while (in_array($generate_SID, array_column($sponsorIDChecker, 'sponsorID')));
$db->insert("accounts", array(
"sponsorID" => $generate_SID
));
The code above will check if a number already exist in the accounts table and if there is an existing, it will generate a random number again until it becomes unique or non-existing in the accounts table. I made the sponsorID field unique so that it won't accept duplicate values.
Now the problem is the code I posted. I thought it would let the $generate_SID be unique because I used the in_array function so it would check if a value already exist in the array and do generate a number again until it is unique but I did receive luckily an error that it tried to insert a random number that already exists and it didn't generate a new one.
Can anyone tell me if there's a solution for this? Or should I re-modify the code above so it would not enter already existing sponsorID? Thank you in advance.
From what i understood, you try to insert a unique id into a table but the generator only runs once and or it tells you that the number already exists.
I've never used slim but it seems in your code you try to do a SELECT of a single record, because you generate a random number and then ask for this number to the database:
$sponsorIDrandom = mt_rand(100000, 999999);
$bindCheck = array(
":sponsorID" => $sponsorIDrandom
);
$sponsorIDChecker = $db->select("accounts", "sponsorID = :sponsorID", $bindCheck);
This only returns one or none rows if as you say the sponsorID is UNIQUE.
And then you try to generate another random number and check if is not repeated based on this single (or null) record.
$generate_SID = null;
do {
$generate_SID = mt_rand(100000, 999999);
} while (in_array($generate_SID, array_column($sponsorIDChecker, 'sponsorID')));
this loop only executes once because the probability of this second random number to be inside this record (if there is a record at all) are almost none and if the database are as big as you says, then the probability for collisions are too high.
for this code to work you need to load every record or ask for the newly generated number if it exists in the database every time it is generated, both alternatives are not recommended but since the databse is already (almost) full.
$sponsorIDChecker = $db->select(...); //use the equivalent of "SELECT sponsorID from accounts" without the WHERE clause, is better to ask for a single column.
$generate_SID = null;
do {
$generate_SID = mt_rand(100000, 999999);
} while (in_array($generate_SID, ...)); //here you put the result of the query above.
$db->insert("accounts", array(
"sponsorID" => $generate_SID
));
Now, something that may be of help: if you set the sponsorID as a zerofill in the databse as you said
I can just set an ID field and add zerofill so it would look like 000001, 000002 and so fort.
then you can lower the min value of mt_rand to 0 and you gain 100000 more IDs to try.
I have the following rows in the database inside url column:
http://some_url/something/34123122.json
http://some_url/something/53124322.json
http://some_url/something/22214322.json
And I want to retrieve them in some function, like this (pseudocode):
function retrieve($ids) {
return $this->fetchAll("SELECT * FROM table WHERE url IN $ids");
}
The problem is that $ids parameter MUST BE an array with ids from those urls only, like:
array(
[0] => 34123122
[1] => 22214322
)
So I have to do something in this function so that I can retrieve rows with urls that contain those ids. How can I do that? Urls can change, but the /******.json ending has always the same pattern.
I don't want to make another query selecting the beginning of the url, it will slow down the application too much.
The proper way to do this is to query only the part of the data that you are interested in - the number. So, you receive an instant +10 to intelligence from performing a quest nearby and you determine that you could create another column to save that number. Your table looks like this now:
CREATE TABLE mytable (
id int not null auto_increment,
url varchar(255) not null,
json_number int not null,
PRIMARY KEY(id),
INDEX(json_number)
) ENGINE = InnoDB;
Before inserting into the table, you use integer sanitizing filter to extract the number from the URL without wasting too much time
Given a URL like this: http://some_url/something/34123122.json you can easily extract the number like this:
$url = 'http://some_url/something/34123122.json';
$number = filter_var($url, FILTER_SANITIZE_NUMBER_INT);
echo $number; // echoes 34123122
And now your query is trivial, you check the json_number column which is also indexed at the same time.
Naturally, you can ignore all I wrote and try other answers which are ugly hacks and worst of all - they're all full table scans.
you will have to use regex in mysql, change your function:
function retrieve($ids) {
$regex = '('.implode('|', $ids).').json$';
return $this->fetchAll("SELECT * FROM table WHERE url REGEXP '$regex'");
}
Note: this is not an optimal solution for large tables. I would suggest you to create an id field in your table and if all ids are unique then you can make id a primary key. Also whenever you insert in that table take out the id part from url and insert it into the id field. In that way you can skip regex. If you are willing to create an id field, then you can execute the following query to update your current table id field:
mysql> update your_table_name set id=replace(substring_index(url, '/', -1), '.json', '');
I do not know if this is a neat solution, but it should work.
function getData($ids) {
foreach($ids as $item) {
$str[] = $item . ".json";
}
$where = "";
foreach($str as $item) {
$where .= "url LIKE '%$item' OR ";
}
return substr("SELECT * FROM table WHERE " . $where, 0, -4);
}
$ids = array(34123122, 53124322, 22214322);
echo getData($ids);
Result:
SELECT * FROM table WHERE url LIKE '%34123122.json' OR url LIKE '%53124322.json' OR url LIKE '%22214322.json'
I think this should do it. Of course you have to run the query aswell.
In a table, the primary field is a Char(12) field called ribiid, whose format is RB##########,
It needs to auto-increment it self, and for that i have prepared the following code:
function getid() {
global $connection;
$idquery = "SELECT ribiid FROM systems ORDER BY ribiid DESC LIMIT 1";
$idsave = mysqli_query($connection, $idquery);
$idresult = mysqli_fetch_assoc($idsave);
$idalpha = substr($idresult['ribiid'], 0, 2);
$idnumeric = substr($idresult, 2);
$newidnumeric = $idnumeric + 1;
$newid = $idalpha . $newidnumeric;
return $newid;
}
Now for testing I manually entered a row in cmd with id = RB0000000000, the next entry that I submit through my webpage using php, should have been RB0000000001, but it is coming RB1.
How can I fix this, this is my first web database. Thanks
Your problem is that when adding 1 to $idnumeric PHP needs to treat it as a number. Leading zeroes in numbers do not make sense, so they are discarded.
To keep the zeroes you can use sprintf format the resulting (incremented) number:
$newid = sprintf("%s%010d", $idalpha, $newidnumeric);
However, using code like this is not a really good idea
There's an issue with this code though: it's subject to a race condition. Consider what could happen if two instances of the script run in parallel:
Instance A Instance B
T |
i | Reads ribiid RB..001 Reads ribiid RB..001
m | Generates next id RB..002 Generates next id RB..002
e v Writes RB..002 to DB
Writes RB..002 to DB => OOPS
As you see this situation will result in instance B failing to insert a record due to the use of a duplicate primary key. To solve this problem you need to eliminate the race condition, which you could do in one of several ways:
Use an AUTO_INCREMENT column for the PK instead of manually inserting values. Although this means you can no longer have the "RB" prefix as part of the key, you can move it to a different column and have the PK be a combination of these two columns.
LOCK TABLES ribiid while the insertion is taking place (note that the lock needs to cover all of the process, not just the getid function). Locking tables is something you normally want to avoid, but if inserts are not frequent it's a usable practical solution.
You could try something like this:
$newid = $idalpha . str_pad($newidnumeric, 10, '0', STR_PAD_LEFT);
This will add zeros to reach the ten chars.
You can padd the numeric string again using the following function:
function pad_number($number, $pad=10){
$pad_zero = $pad - strlen($number.'');
$nstr = '';
for($i =0; $i< $pad_zero; $i++){
$nstr .="0";
}
$nstr .= $number;
return $nstr;
}
You can use in your code this function as:
$newid = $idalpha . pad_number($newidnumeric);