Generate random number while inserting row - php

I want to insert data in a table called accounts, and one of the fields gotta have a random number, starting with 11 and be 9 digits long, for example 112345673 or 119876543. Is this possible through SQL or I have to do this in PHP then insert there?

If you're inserting one row at a time...
--INSERT dbo.table(column1, random_column)
SELECT 'column1_value',
'11' + RIGHT(REPLACE(CONVERT(VARCHAR(12), RAND()), '.', ''), 3)
+ RIGHT(REPLACE(CONVERT(VARCHAR(12), RAND()), '.', ''), 4);
If this is part of a multirow SELECT, you will see the same number assigned to each row, so you'd need to randomize based on other data in each row (or using NEWID() as Conrad pointed out), e.g.
--INSERT dbo.table(column1, random_column)
SELECT name, '11' + RIGHT(1000000 + ABS(CONVERT(BIGINT,
CONVERT(VARBINARY(16), NEWID()))), 7)
FROM sys.all_objects;
(I've commented out the INSERT so you can test the result independently.)

This would give you an 11 digit random number. In case the random number is shorter than 9 digits, it is prefixed with zeroes, so the overall length is always 11:
select '11' + right('11000000000' +
cast(abs(convert(bigint,convert(varbinary(8),newid()))) as varchar(20)), 9)

Maybe this is overly simplistic, but 110000000 + (ABS(CHECKSUM(NEWID())) % 10000000) would be a very lightweight way to produce what you want.
So
ALTER TABLE mySchema.myTable ADD CONSTRAINT DF_myColumn
DEFAULT 110000000 + (ABS(CHECKSUM(NEWID())) % 10000000) FOR myColumn

Use PHP.
$randnum = "11".rand(1000000, 9999999);
Then just insert that in the query, and voila.

Possible but not great performance. Try this.
insert into table2(randomNumberColumn)
select convert(bigint,'11' + convert(varchar(100),convert(bigint,right(convert(varchar(100),abs(convert(int,convert(varbinary(16),newid())))),7)))) as a
from table1
One issue with this is it doesn't guarantee length of 11 digits. Sometimes it will be 10 due to the smallness of the number. Haven't really looked into how to fill in the extra 0 when Random Number Generator comes up with a small number.

I would try:
SELECT '11'+REPLACE(STR(RAND(),9,7),'0.','')
...or this slight variation...
SELECT '11'+RIGHT(STR(RAND(),9,7),7)
...whichever proves faster.

Related

How can I format a number with leading zeros and thousands commas?

I have an int field in a MySQL database that is progressively going up every time a user of the database performs a specific action so it's going up in increments of 1.
When displaying this on a php page, I want it to display this value as 0,000,001 rather than it's raw data of just 1.
How do I format the number to display like this? I've never had to do it before, so I'm bewildered.
Something like this should work:
SELECT INSERT(INSERT(LPAD([value], 7, '0'), 5, 0, ','), 2, 0, ',')
FROM ....
LPAD returns a string of length 7, with leading 0's. If [value] is longer than 7 it will be truncated (from the right); so '12345678' becomes '1234567'.
The two INSERT calls insert the commas.
Edit: Changed the 1's to 0's; apparently they effectively made it a "replace" rather than an "insert".
I'd suggest doing this in code though, instead of a query, if you can.
If you would rather do it in the php, something like:
$pretty = substr(number_format(10000000 + $counter), 1);
this will work as long as counter never exceeds 9,999,999.

How to search increasing values in MySQL if column value has custom template?

I have varchar column type. Here is example of column value - "A - 474, B - 709, C - 397, D - 48"
If i want to search EXACT number everything is ok. I use php and query -
$note[$key][0] = "B";
$note[$key][1] = "709";
"SELECT 1 FROM example WHERE column LIKE '%".$note[$key][0]." - ".$note[$key][1]."%"
But how can i modify query to search increasing values?
I want something like that:
"SELECT 1 FROM example WHERE column LIKE '%".$note[$key][0]." and value >= 50"%"
I think RLIKE may be the solution. Is that right? Is there performance issues?
You can split the value. I would say that substring_index() is the way to go:
where column = '$key[0]%' and
substring_index(column, ' - ', -1) + 0 >= 50
Note: you should start using parameterized queries instead of embedding user-supplied values into the query string.

AUTO_INCREMENT implementation for string column containing a special number format

Context and goal
In table clients I have a column clientNum CHAR(11) NOT NULL with UNIQUE KEY constraint. It contains client number in the format xxx-xxx-xxx where x is a decimal digit. For more details on the format see below.
I want to implement something like AUTO_INCREMENT for this column so that each client gets their number calculated automatically. From MySQL CREATE TABLE docs:
An integer or floating-point column can have the additional attribute AUTO_INCREMENT. When you insert a value of NULL (recommended) or 0 into an indexed AUTO_INCREMENT column, the column is set to the next sequence value. Typically this is value+1, where value is the largest value for the column currently in the table. AUTO_INCREMENT sequences begin with 1.
So I want to find the next number available and use it as clientNum value for newly inserted client row. Next number available is current maximum of clientNum incremented.
I’m coding in PHP using PDO to access the MySQL database (see PDO Tutorial for MySQL Developers).
Client number format
As stated above, the client number is in format xxx-xxx-xxx where x is a decimal digit. The range of each segment is 000 to 999. It is basically a 9-digit integer with leading zeroes and dash as thousands separator. It cannot get above 999-999-999.
Currently we want it be even more restricted, specifically in format 000-1xx-xxx (between 000-100-000 and 000-199-999). But there are already some numbers in the database that can start anywhere from 000-000-001 to 500-000-000.
Unfortunately it has to be stored in this format, I cannot change it.
Finding maximum
I need to get the max number in range 000-100-000 to 000-199-999, values outside this range must be ignored. This is where my problem comes in because as said before some numbers already exist above this.
Maximum is never 000-199-999. Otherwise in would result in adding 000-200-000 and the next time called maximum will be 000-199-999 again, resulting in attempt to insert 000-200-000 again.
How incrementation works
In PHP in can be done like this:
$clientNum = "000-100-000";
$clientNum = str_replace("-", "", $clientNum);
$clientNum++;
$clientNum = implode("-", str_split(str_pad($clientNum, 9, "0", STR_PAD_LEFT), 3));
Final $clientNum value is 000-100-001.
When the initial number is 000-120-015 then the code above produces 000-120-016. Overflow propagates to the next segment, i.e. 000-100-999 becomes 000-101-000. 999-999-999 cannot be incremented.
Idea to start with
In a loop I want to get the next number available, check if that number exists in the database, and if so, redo that loop until it finds an unused number. I know how to check if it’s in the database the first time, but I’m not sure how to do the loop.
Does anyone know a way to do this?
You may want to solve this in SQL, because otherwise you need two transactions (one for reading, one for writing) and meanwhile the number could be used by a concurrent access.
In MySQL, you can use this SQL reimplementation of your PHP code:
INSERT(INSERT(LPAD(CAST(CAST(REPLACE(clientNum, '-', '') as UNSIGNED) + 1 as CHAR), 9, '0'), 7, 0, '-'), 4, 0, '-')
This increments 000-000-999 to 000-001-000 and 999-999-999 to 100-000-000 (truncated from 100-000-0000 by LPAD()). I warned you.
E.g. to just preview what the next value is, use
SELECT INSERT(INSERT(LPAD(CAST(CAST(REPLACE(clientNum, '-', '') as UNSIGNED) + 1 as CHAR), 9, '0'), 7, 0, '-'), 4, 0, '-') FROM clients
If you want to use this when inserting a new row, it is used like this:
INSERT
INTO clients(clientNum, name)
SELECT
INSERT(INSERT(LPAD(CAST(
COALESCE(MAX(CAST(REPLACE(clientNum, '-', '') AS UNSIGNED)), 0) + 1
AS CHAR), 9, '0'), 7, 0, '-'), 4, 0, '-'),
'John Doe'
FROM clients
This works regardless of what API you use to access the database, as long as it is MySQL database. The database does the computation. However, it does not work if clients is a temporary table, which I expect it not to be. More on that below.
See also string functions, CAST(), COALESCE() and INSERT … SELECT in MySQL manual.
Later you added that the permitted values are from range 000-100-000 to 000-199-999. Other values shall be ignored for the purpose of finding maximum. A WHERE clause must be added to the SELECT part of INSERT written above.
INSERT
INTO clients(clientNum, name)
SELECT
INSERT(INSERT(LPAD(CAST(
COALESCE(MAX(CAST(REPLACE(clientNum, '-', '') AS UNSIGNED)), 0) + 1
AS CHAR), 9, '0'), 7, 0, '-'), 4, 0, '-'),
'John Doe'
FROM clients
WHERE clientNum BETWEEN '000-100-000' AND '000-199-999'
Then you stated that my solution does not work for you and proposed a supposed fix:
INSERT
INTO clients(clientNum, name)
VALUES
(SELECT
INSERT(INSERT(LPAD(CAST(
COALESCE(MAX(CAST(REPLACE(clientNum, '-', '') AS UNSIGNED)), 0) + 1
AS CHAR), 9, '0'), 7, 0, '-'), 4, 0, '-')
FROM clients AS tmptable
WHERE clientNum BETWEEN '000-100-000' AND '000-199-999'),
'John Doe'
This uses a subquery instead of the INSERT … SELECT syntax.
In MySQL, table cannot be modified (by INSERT in this case) and read by a subquery at the same time. Quoting the subquery manual:
In MySQL, you cannot modify a table and select from the same table in a subquery. This applies to statements such as DELETE, INSERT, REPLACE, UPDATE, and (because subqueries can be used in the SET clause) LOAD DATA INFILE.
However, you found a workaround using a temporary table. A temporary table is used when an alias (in this case clients AS tmptable) is defined, which evades reading from and writing to the same table at the same time. You used temporary table to store the original table, the article describing the workaround uses it to store the result of the subquery (which is more efficient, I guess). Both approaches work.
At this point I want to point out that my solution should work (and works for me!) too except for the improbable case when clients is a temporary table. I think I can expect it not to be one. Quoting the INSERT … SELECT manual page:
When selecting from and inserting into a table at the same time, MySQL creates a temporary table to hold the rows from the SELECT and then inserts those rows into the target table. However, it remains true that you cannot use INSERT INTO t ... SELECT ... FROM t when t is a TEMPORARY table, because TEMPORARY tables cannot be referred to twice in the same statement (see Section C.5.7.2, “TEMPORARY Table Problems”).
As for me this is explicitly saying that my original approach using INSERT … SELECT should work.
Just to provide a complete answer, I’ll address your original request for PHP solution using database polling. Once more I must add that this is certainly not a good solution.
Your clientNum column must be a unique key. You need to repeat the following steps until successful update:
Get the current maximum of clientNum.
Increment the obtained value.
Try to insert the row.
If successful, finish, otherwise throw the clientNum max value away and loop.
The insertion will fail due to violation of the aforementioned unique key constraint. This happens when another connection to the database successfully performs an insert in the meantime between steps 1. and 3..
You should prepare the statement outside the loop using PDO::prepare() and then execute it in the loop. The return value of execute method indicates success (true) or failure (false).
This is enough info to implement step 3.. Steps 1. and 2. consist of fetching the result of
SELECT MAX(clientNum) FROM clients
and running it through the code provided by Stephanus Yanaputra. Step 4. is a simple loop condition using the return value from execution of INSERT query in step 3..
<?php
mysql_connect(....);
mysql_select_db($db_name);
$res=mysql_query("select ClientNum from ClientTable");
$name_arr=array();
while($row=mysql_fetch_array($res))
{
foreach($row as $name)
$name_arr[]=$name;
}
$clientNum="000-000-000";
while(true){
$clientNum = str_replace("-", "", $clientNum);
$clientNum++;
if($clientNum>999999999)
{
echo("No mismatch found");
break;
}
$clientNum = implode("-", str_split($clientNum, 3));
if(!in_array($clientNum, $name_arr))
{
echo "The first unmatched clientNum is:".$clientNum;//This is what you want.
break;
}
}
?>
Comments
The query execution only once i.e. outside the while loop makes it less time complex. The time complexity is reduced due to use of array instead of execution of the query itself multiple times because searching in an array is comparatively very less time complex than searching in the database.
An easy solution based on function that you provided.
Change the function name rawr() to any naming that you like. (I couldn't find the best name and ended up using some gibberish name lol).
function rawr($in)
{
$num = str_replace("-", "", $in);
$num++;
// Convert back
$str = (string) $num;
// Add Leading 0
while(strlen($str) < 9)
{
$str = "0" . $str;
}
echo $str . "<br />";
$final = substr($str,0,3) . "-" . substr($str,3,3) . "-" . substr($str,6,3);
return $final;
}
To test it, try this code:
echo rawr(0);
echo "<br />";
echo rawr("000-000-000");
echo "<br />";
echo rawr("012-345-678");
echo "<br />";
echo rawr("123-456-789");
echo "<br />";
This will give an output that you desire. However you will have to code it yourself to test the database. In my opinion, this is not the best way to solve your problem, but it should work :)

Unique auto increment reference number that resets at the 1st of each month

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;

mysql between question

For the mysql "between" operator, is it necessary for the before and after value to be numerically in order?
like:
BETWEEN -10 AND 10
BETWEEN 10 AND -10
Will both of these work or just the first one?
Also, can I do:
WHERE thing<10 AND thing>-10
Will that work or do I have to use between?
Lastly, can I do:
WHERE -10<thing<10
?
BETWEEN -10 AND 10
This will match any value from -10 to 10, bounds included.
BETWEEN 10 AND -10
This will never match anything.
WHERE thing<10 AND thing>-10
This will match any value from -10 to 10, bounds excluded.
Also, if thing is a non-deterministic expression, it is evaluated once in case of BETWEEN and twice in case of double inequality:
SELECT COUNT(*)
FROM million_records
WHERE RAND() BETWEEN 0.6 AND 0.8;
will return a value around 200,000;
SELECT COUNT(*)
FROM million_records
WHERE RAND() >= 0.6 AND RAND() <= 0.8;
will return a value around 320,000
The min value must come before the max value. Also note that the end points are included, so BETWEEN is equivalent to:
WHERE thing>=-10 AND thing<=10
Please keep it to one question per post. Anyway:
http://dev.mysql.com/doc/refman/5.0/en/comparison-operators.html#operator_between
BETWEEN min AND max, in that order.
from the link:
This is equivalent to the expression (min <= expr AND expr <= max) if
all the arguments are of the same type
The second alternative will also work, of course.
First question:
Will both of these work or just the first one?
yes,both of these work
Second question:
Will that work or do I have to use between?
it also valid but as you can see just empty result
Yes your between must be in order to return the excepted result.
Let's say you have a table with a row called mynumber that contains 10 rows :
MyNumber
--------
1
2
3
4
5
6
7
8
9
10
So
select * from thistable table where table.myNumber BETWEEN 1 and 5
will return
1
2
3
4
5
but
select * from thistable table where table.myNumber BETWEEN 5 and 1
return nothing.
Your 2nd question : yes it is the same thing. but beware in you example you will have to put <= and >= to be the same as between. if not, in our example, you would get
2
3
4
Hope it help
I've already seen such things work with integers :
WHERE -10
But it's better to avoid it. One reason is that it doesn't seem to work well with other types. And MySQL doesn't issue any warning.
I've tried it with datetime columns, and the result was wrong.
My request looked like this one:
SELECT *
FROM FACT__MODULATION_CONSTRAINTS constraints
WHERE constraints.START_VALIDITY<= now() < constraints.END_VALIDITY
The result was not as expected. I got twice as many results as the same request with two inequalities (which returned correct results). Only the 1st part of the expression evaluated correctly.

Categories