I'm trying to create an increment number licence with the current date of the year + an increment number but i really don't know how to do this i know MYSQl does not support sequences but i would like to know if there is a way to solve the problem
here my controller
public function create(){
$licence = new Licence ;
$licence ->num_licence = Carbon::now()->year -- i would like here to put the current year like 2017 with a random unique number to get the format like 20170001 for exemple !
...
how to acheice this? thanks in advance :)
you can use uniqid function with current year as prefix.
public function create(){
$licence = new Licence ;
$num_liscence_exist=true;
while($num_liscence_exist){
$num_liscence=uniqid(Carbon::now()->year);
if (!Liscence_Table::where('num_liscence', '=',"'".$num_liscence."'")->exists()) {
$liscence->num_liscence=$num_liscence;
$num_liscence_exist=false;
}
}
}
Generate the random id using uniqid() and concatenate with date:
public function create(){
$liscence=new Liscence();
$year = Carbon::now()->year;
$liscence->num_liscence= $year. uniqid();
$liscence->save();
}
using uniqid() get unique number or also you can use time stamp with it:
$liscence=new Liscence();
$year = Carbon::now()->year;
$liscence->num_liscence= $year. strtoupper(uniqid()) . Carbon::now()->timestamp;
$liscence->save();
Will look like : 2017ABC011486543961
A sequence needs to be stored somewhere so I doubt there's a sensible pure-PHP solution. But since you're already using MySQL there's no reason to not use it. I'd create a database table for that, something like:
CREATE TABLE licence_sequence (
sequence_year YEAR NOT NULL,
next_value INT UNSIGNED NOT NULL DEFAULT 1,
PRIMARY KEY (sequence_year, next_value)
)
ENGINE=InnoDB;
(Ugly sequence_year name was chosen to avoid having to quote it every time.)
You then have to manage this from code, including:
Ensuring you have rows for every year as you need them (this can be as simple as populating data for all years for next century).
Ensuring you don't assign the same value to different items.
A rough overview of stuff to consider:
You can (and should) make your licence number a unique index to avoid dupes.
You can use transactions to avoid unnecessary gaps. Transaction should wrap all the operations involved:
Get next sequence number
Save it into new licence
Increment next number
You need to ensure atomic operations in a multi-tasking environment. This is the hardest part. I've been lately being playing with MySQL named locks and they seem to work correctly.
Related
I'm using PHP 7 with Phalcon PHP and I'm trying to create a method to generate a booking number. Here is my current method :
public function generateNumber($company_code) {
// Build the prefix : COMPANY20190820
$prefix = $company_code . date('Ymd');
// It's like SELECT count(*) FROM bookings WHERE number LIKE 'COMPANY20190820%'
$counter = Bookings::count(array(
"number LIKE :number:",
"bind" => array('number' => $prefix.'%')
));
// Concat prefix with bookings counter with str_pad
// COMPANY20190820 + 005 (if 4 bookings in DB)
$booking_number = $prefix . str_pad($counter + 1, 3, 0, STR_PAD_LEFT);
// Return COMPANY20190820005
return $booking_number;
}
So I have a problem because sometime I have to delete 1 or multiple bookings so I can get :
COMPANY20190820001
COMPANY20190820002
COMPANY20190820005
COMPANY20190820006
COMPANY20190820007
And I need to add after the last in my DB so here 007, because I can get duplicated booking number if I count like that.
So how can I do to take the last and increment according the last booking number of the current day ?
You need to rethink what you want to do here as it will never work that way.
As I see it you have at least two options:
Use an auto-increment id and use that in combination with the prefix
Use a random fairly unique string (e.g. UUID4)
You should never manually try to get the current maximum id as that may and most likely will at some point result in race conditions and brittle code as a result of that.
So I found a solution, maybe there is a better way to do that but my function works now:
public function generateNumber($company_code) {
// Build the prefix : COMPANY20190820
$prefix = $company_code . date('Ymd');
// Get the last booking with the today prefix
// e.g : COMPANY20190820005
$last_booking = Bookings::maximum(array(
"column" => "number",
"number LIKE :number:",
"bind" => array('number' => $prefix.'%')
));
// Get the last number by removing the prefix (e.g 005)
$last_number = str_replace($prefix, "", $last_booking);
// trim left 0 if exist to get only the current number
// cast to in to increment my counter (e.g 5 + 1 = 6)
$counter = intval(ltrim($last_number, "0")) + 1;
// Concat prefix + counter with pad 006
$booking_number = $prefix . str_pad($counter, 3, 0, STR_PAD_LEFT);
// Return COMPANY20190820006
return $booking_number;
}
I reckon that the use case you describe does not justify the hassle of writing a custom sequence generator in PHP. Additionally, in a scenario where booking deletion is expected to happen, ID reusing feels more a bug than a feature, so your system should store a permanent counter to avoid reusing, making it less simple. Don't take me wrong, it can be done and it isn't rocket science, but it's time and energy you don't need to spend.
Your database engine surely has a native tool to generate autoincremented primary keys, with varying names and implementations (SQL Server has identity, Oracle has sequences and identity, MySQL has auto_increment...). Use that instead.
Keep internal data and user display separated. More specifically, don't use the latter to regenerate the former. Your COMPANY20190820007 example is trivial to compose from individual fields, either in PHP:
$booking_number = sprintf('%s%s%03d',
$company_code,
$booking_date->format('Ymd'),
$booking_id
);
... or in SQL:
-- This is MySQL dialect, other engines use their own variations
SELECT CONCAT(company_code, DATE_FORMAT(booking_date, '%Y%m%d'), LPAD(booking_id, 3, '0')) AS booking_number
FROM ...
You can (and probably should) save the resulting booking_number, but you cannot use it as source for further calculations. It's exactly the same case as dates: don't need to store dates in plain English in order to eventually display them to the end-user and you definitively don't want to parse English dates back to actual dates in order to do anything else beyond printing.
You also mention the possibility of generating long pure-digit identifiers, as Bookings.com does. There're many ways to do it and we can't know which one they use, but you may want to considering generating a numeric hash out of your auto-incremented PK via integer obfuscation.
you could split your database field in two parts, so you hold the prefix and the counter separately.
then, you simply select the highest counter for your desired prefix and increment that one.
if you can't change the table structure, you could alternatively order by the id descendingly and select the first. then you can extract its counter manually. keep in mind you should pad the numbers then, or you get #9 even if #10 exists.
if padding is not an option, you can direct the database to replace your prefix. that way, you can cast the remaining string to a number and let the database sort - this will cost some performance, though, so keep the amount of records low.
Can I use strtotime("now") as a unique ID in php application as I assume it uses current timestamp and cannot generate the same integer in future?
It is not safe to use strtotime('now') as id
take a look at this code:
$now = strtotime('now');
$anotherNow = strtotime('now');
$sameCounter = 0;
while($anotherNow == $now){
$sameCounter++;
$anotherNow = strtotime('now');
}
echo $sameCounter; //8558
Result is 8558, very not safe, php did 8558 operations during that secondThere's big chance that you'll get the same ID few times.
you can use uniq function instead
Uniq id is itself a php function , you can have this like this
$uniq = uniqid();
you can add more entropy in this
No, don't do that. Although the timestamp is likely to always be different that doesn't mean it's unique. For instance, what would happen if two requests are issued at the same time? You will have two equal timestamp. When choosing unique IDs, you have to be sure that there's no possible cases in which there are equals values. A good solution is having an auto-increment field. That will make sure that your id will never be the same.
I'm using for small projects, like as mini blog or cms.
this function generate 10 digits with unix time
and change every second, if your project not biq and not get high request or inserts you can use this safely.
$UniqID = time();
it create ID with 10 digits like (1462570078)
for other project I use microtime more than time() or other uniq like this
$m=microtime(true);
echo sprintf("%8x%05x\n",floor($m),($m-floor($m))*10000);
My be rare chance we can get duplicates , you can prefix username
Like this echo "$username-".time(); or "$username".time(); or "$userid-".time();
In php already have uniqid .
The uniqid() function generates a unique ID based on the microtime
(current time in microseconds).
For more information please refer official website http://php.net/manual/en/function.uniqid.php
I have a database table with various fields involving jobs done on ships including a field named created which uses DATE format. The result i want to achieve is to have a unique reference number for each job. The format i want to use for this reference number is:
example : Lets say the date of the job is 23/11/2013 like today. Then the number would be 1311/1 the next job 1311/2 and goes on. If the month changes and the date of the next job is for example 15/12/2013 the refence number i would like to have if its the first job of the month is 1312/1.
So the two first digits of my reference number would show the year,the next two the month and the number after the slash i would like it to be an auto_increment number that will reset each month.My code so far is :
$job_num = 1;
foreach($random as $rand) {
$vak = $rand->created;
$gas = $rand->id;
$vak1 = substr($vak, 2, 2);
$vak2 = substr($vak, 5, -3);
$vak3 = substr($vak, 8, 10);
if(date(j) > 1) {
echo $vak1.$vak2.'/'.$job_num.'<br>';
$job_num++;
} else {
$job_num = 1;
echo $vak1.$vak2.'/'.$job_num.'<br>';
$job_num++;
}
}
So as u can see i want to achieve all this inside a foreach statement. And although the above code kinda works,the porblem i have is that at the 1st of any month in other words when date(j) = 1 if i insert more than one job in my database the $job_num variable resets as many times as the jobs i have inserted resulting in identical refence numbers.
I am really new in programming and php so if anyone could help me solve this, i would really appreciate it.
Thanks in advance:)
You can't do this with the auto-increment mechanism if you use InnoDB, which is MySQL's default storage engine.
You can do it with the MyISAM storage engine, but you really shouldn't use MyISAM, for many reasons.
So you'll have to assign the repeating numbers yourself. This means you have to lock the table while you check what is the current maximum number for the given month, then insert a new row with the next higher number.
If that seems like it would impair concurrent access to the table, you're right. Keep in mind that MyISAM does a table-lock during insert/update/delete of any row.
If you can use the MyISAM engine, you can get this behavior without procedural code.
create table demo (
yr_mo integer not null,
id integer auto_increment,
other_columns char(1) default 'x',
primary key (yr_mo, id)
) engine=MyISAM;
insert into demo (yr_mo) values (1311);
insert into demo (yr_mo) values (1311);
insert into demo (yr_mo) values (1311);
insert into demo (yr_mo) values (1311);
insert into demo (yr_mo) values (1312);
The last INSERT statement starts a new month.
Now if you look at the autoincrement values the MyISAM engine assigned . . .
select * from demo;
YR_MO ID OTHER_COLUMNS
--
1311 1 x
1311 2 x
1311 3 x
1311 4 x
1312 1 x
This is MyISAM's documented behavior; look for "MyISAM Notes".
If you want the form yymm/n for presentation, use something like this.
select concat(yr_mo, '/', id) as cat_key
from demo;
I am developing a mysql database.
I "need" a unique id for each user but it must not auto increment! It is vital it is not auto increment.
So I was thinking of inserting a random number something like mt_rand(5000, 1000000) into my mysql table when a user signs up for my web site to be. This is where I am stuck?!
The id is a unique key on my mysql table specific to each user, as I can not 100% guarantee that inserting mt_rand(5000, 1000000) for the user id will not incoherently clash with another user's id.
Is there a way in which I can use mt_rand(5000, 1000000) and scan the mysql database, and if it returns true that it is unique, then insert it as the user's new ID, upon returning false (somebody already has that id) generate a new id until it becomes unique and then insert it into the mysql database.
I know this is possible I have seen it many times, I have tried with while loops and all sorts, so this place is my last resort.
Thanks
You're better off using this: http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_uuid
Or using this: http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html
But if you actually want to do what you are saying, you can just do something like:
$x;
do {
$x = random_number();
"SELECT count(*) FROM table WHERE id = $x"
} while (count != 0);
// $x is now a value that's not in the db
You could use a guid. That's what I've seen done when you can't use an auto number.
http://php.net/manual/en/function.com-create-guid.php
Doesn't this function do what you want (without verification): http://www.php.net/manual/en/function.uniqid.php?
I think you need to approach the problem from a different direction, specifically why a sequence of incrementing numbers is not desired.
If it needs to be an 'opaque' identifier, you can do something like start with a simple incrementing number and then add something around it to make it look like it's not, such as three random numbers on the end. You could go further than that and put some generated letters in front (either random or based on some other algorithm, such as the day of the month they first registered, or which server they hit), then do a simple checksuming algorithm to make another letter for the end. Now someone can't easily guess an ID and you have a way of rejecting one sort of ID before it hits the database. You will need to store the additional data around the ID somewhere, too.
If it needs to be a number that is random and unique, then you need to check the database with the generated ID before you tell the new user. This is where you will run into problems of scale as too small a number space and you will get too many collisions before the check lucks upon an unallocated one. If that is likely, then you will need to divide your ID generation into two parts: the first part is going to be used to find all IDs with that prefix, then you can generate a new one that doesn't exist in the set you got from the DB.
Random string generation... letters, numbers, there are 218 340 105 584 896 combinations for 8 chars.
function randr($j = 8){
$string = "";
for($i=0;$i < $j;$i++){
srand((double)microtime()*1234567);
$x = mt_rand(0,2);
switch($x){
case 0:$string.= chr(mt_rand(97,122));break;
case 1:$string.= chr(mt_rand(65,90));break;
case 2:$string.= chr(mt_rand(48,57));break;
}
}
return $string;
}
Loop...
do{
$id = randr();
$sql = mysql_query("SELECT COUNT(0) FROM table WHERE id = '$id'");
$sql = mysql_fetch_array($sql);
$count = $sql[0];
}while($count != 0);
For starters I always prefer to do all the randomization in php.
function gencode(){
$tempid=mt_rand(5000, 1000000);
$check=mysql_fetch_assoc(mysql_query("SELECT FROM users WHERE id =$tempid",$link));
if($check)gencode();
$reg=mysql_query("INSERT INTO users id VALUES ('$tempid')",$link);
//of course u can check for if $reg then insert successfull
I'm programming a script using PHP and MySQL and I want to get a
unique id (consisting of a string: capitals and small
letters with numbers) like: gHYtUUi5b.
I found many functions in PHP that can generate such numbers but I'm afraid about how to ensure the id is unique!
UPDATE: uuid is long, I mean such id like: (P5Dc) an 11 alphanumeric char.
EDIT: This answer has been flagged for being dangerous in the context of destroying a database. Do NOT use this code to generate unique ids in databases!
I use UUID() to create a unique value.
example:
insert into Companies (CompanyID, CompanyName) Values(UUID(), "TestUUID");
You may like the way that we do it. I wanted a reversible unique code that looked "random" -a fairly common problem.
We take an input number such as 1,942.
Left pad it into a string: "0000001942"
Put the last two digits onto the front: "4200000019"
Convert that into a number: 4,200,000,019
We now have a number that varies wildly between calls and is guaranteed to be less than 10,000,000,000. Not a bad start.
Convert that number to a Base 34 string: "2oevc0b"
Replace any zeros with 'y' and any ones with 'z': "2oevcyb"
Upshift: "2OEVCYB"
The reason for choosing base 34 is so that we don't worry about 0/O and 1/l collisions. Now you have a short random-looking key that you can use to look up a LONG database identifier.
A programmatic way can be to:
add a UNIQUE INDEX to the field
generate a random string in PHP
loop in PHP ( while( ! DO_THE_INSERT ) )
generate another string
Note:
This can be dirty, but has the advantage to be DBMS-agnostic
Even if you choose to use a DBMS specific unique ID generator function (UUID, etc)
it is a best practice to assure the field HAS to be UNIQUE, using the index
the loop is statistically not executed at all, it is entered only on insert failure
If you use MySQL with version higher than 5.7.4, you can use the newly added RANDOM_BYTES function:
SELECT TO_BASE64(RANDOM_BYTES(16));
This will result in a random string such as GgwEvafNLWQ3+ockEST00A==.
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.
Use UUID function.
I don't know the source of your procedures in PHP that generates unique values. If it is library function they should guarantee that your value is really unique. Check in documentation. You should, hovewer, use this function all the time. If you, for example, use PHP function to generate unique value, and then you decide to use MySQL function, you can generate value that already exist. In this case putting UNIQUE INDEX on the column is also a good idea.
DELIMITER $$
USE `temp` $$
DROP PROCEDURE IF EXISTS `GenerateUniqueValue`$$
CREATE PROCEDURE `GenerateUniqueValue`(IN tableName VARCHAR(255),IN columnName VARCHAR(255))
BEGIN
DECLARE uniqueValue VARCHAR(8) DEFAULT "";
DECLARE newUniqueValue VARCHAR(8) DEFAULT "";
WHILE LENGTH(uniqueValue) = 0 DO
SELECT CONCAT(SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', RAND()*34+1, 1),
SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', RAND()*34+1, 1),
SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', RAND()*34+1, 1),
SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', RAND()*34+1, 1),
SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', RAND()*34+1, 1),
SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', RAND()*34+1, 1),
SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', RAND()*34+1, 1),
SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', RAND()*34+1, 1)
) INTO #newUniqueValue;
SET #rcount = -1;
SET #query=CONCAT('SELECT COUNT(*) INTO #rcount FROM ',tableName,' WHERE ',columnName,' like ''',newUniqueValue,'''');
PREPARE stmt FROM #query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
IF #rcount = 0 THEN
SET uniqueValue = #newUniqueValue ;
END IF ;
END WHILE ;
SELECT uniqueValue;
END$$
DELIMITER ;
And call the stored procedure as GenerateUniqueValue('tableName','columnName'). This will give you a 8 digit unique character everytime.
To get unique and random looking tokens you could just encrypt your primary key i.e.:
SELECT HEX(AES_ENCRYPT(your_pk,'your_password')) AS 'token' FROM your_table;
This is good enough plus its reversable so you'd not have to store that token in your table but to generate it instead.
Another advantage is once you decode your PK from that token you do not have to do heavy full text searches over your table but simple and quick PK search.
Theres one small problem though. MySql supports different block encryption modes which if changed will completely change your token space making old tokens useless...
To overcome this one could set that variable before token generated i.e.:
SET block_encryption_mode = 'aes-256-cbc';
However that a bit waste... The solution for this is to attach an encryption mode used marker to the token:
SELECT CONCAT(CONV(CRC32(##GLOBAL.block_encryption_mode),10,35),'Z',HEX(AES_ENCRYPT(your_pk,'your_password'))) AS 'token' FROM your_table;
Another problem may come up if you wish to persist that token in your table on INSERT because to generate it you need to know primary_key for the record which was not inserted yet... Ofcourse you might just INSERT and then UPDATE with LAST_INSERT_ID() but again - theres a better solution:
INSERT INTO your_table ( token )
SELECT CONCAT(CONV(CRC32(##GLOBAL.block_encryption_mode),10,35),'Z',HEX(AES_ENCRYPT(your_pk,'your_password'))) AS 'token'
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = "your_table";
One last but not least advantage of this solution is you can easily replicate it in php, python, js or any other language you might use.
Below is just for reference of numeric unique random id...
it may help you...
$query=mysql_query("select * from collectors_repair");
$row=mysql_num_rows($query);
$ind=0;
if($row>0)
{
while($rowids=mysql_fetch_array($query))
{
$already_exists[$ind]=$rowids['collector_repair_reportid'];
}
}
else
{
$already_exists[0]="nothing";
}
$break='false';
while($break=='false'){
$rand=mt_rand(10000,999999);
if(array_search($rand,$alredy_exists)===false){
$break='stop';
}else{
}
}
echo "random number is : ".$echo;
and you can add char with the code like -> $rand=mt_rand(10000,999999) .$randomchar; // assume $radomchar contains char;
For uniqueness what I do is I take the Unix timestamp and append a random string to it and use that.
<?php
$hostname_conn = "localhost";
$database_conn = "user_id";
$username_conn = "root";
$password_conn = "";
$conn = mysql_pconnect($hostname_conn, $username_conn, $password_conn) or trigger_error(mysql_error(),E_USER_ERROR);
mysql_select_db($database_conn,$conn);
// run an endless loop
while(1) {
$randomNumber = rand(1, 999999);// generate unique random number
$query = "SELECT * FROM tbl_rand WHERE the_number='".mysql_real_escape_string ($randomNumber)."'"; // check if it exists in database
$res =mysql_query($query,$conn);
$rowCount = mysql_num_rows($res);
// if not found in the db (it is unique), then insert the unique number into data_base and break out of the loop
if($rowCount < 1) {
$con = mysql_connect ("localhost","root");
mysql_select_db("user_id", $con);
$sql = "insert into tbl_rand(the_number) values('".$randomNumber."')";
mysql_query ($sql,$con);
mysql_close ($con);
break;
}
}
echo "inserted unique number into Data_base. use it as ID";
?>
crypt() as suggested and store salt in some configuration file, Start salt from 1 and if you find duplicate move to next value 2. You can use 2 chars, but that will give you enough combination for salt.
You can generate string from openssl_random_pseudo_bytes(8). So this should give random and short string (11 char) when run with crypt().
Remove salt from result and there will be only 11 chars that should be enough random for 100+ millions if you change salt on every fail of random.
You might also consider using crypt()* to generate a [nearly-guaranteed] unique ID inside your contraints.
USE IT
$info = random_bytes(16);
$info[6] = chr(ord($info[6]) & 0x0f | 0x40);
$info[8] = chr(ord($info[8]) & 0x3f | 0x80);
$result =vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($info), 4));
return $result;
This generates random ids:
CREATE TABLE Persons (
ID Integer PRIMARY KEY AUTOINCREMENT,
LastName varchar(255) NOT NULL,
FirstName varchar(255),
Age int
);
You could use Twitter's snowflake.
In short, it generates a unique id based on time, server id and a sequence. It generates a 64-bit value so it is pretty small and it fits in an INT64. It also allows for sorting values correctly.
https://developer.twitter.com/en/docs/basics/twitter-ids
In sum, it allows multiple servers, highly concurrency, sorting value and all of them in 64 bits.
Here it is the implementation for MySQL
https://github.com/EFTEC/snowflake-mysql
It consists of a function and a table.