I'm trying to generate select names from a mysql database by using the rand() function in php. The program works fine but is it possible to make the results more random because some of the records that I'm getting is the same(whenever I clear the session and start over again), and others might not get selected. I have currently 91 records in the table.
if(!isset($_SESSION['max'])){
$max = $db->get_var("SELECT COUNT(*) FROM tbl_participants");
$_SESSION['max'] = $max;
}else{
$max = $_SESSION['max'];
}
if(!empty($_GET['clear'])){
$_SESSION['used_up'] = Array();
$_SESSION['names'] = Array();
}
//print_r($_SESSION['used_up']);
$current_number = rand(0, $max);
$exists = check_existing($current_number, $_SESSION['used_up']);
if($exists == 2){
$_SESSION['used_up'][] = (int)$current_number;
$name = $db->get_var("SELECT participant FROM tbl_participants WHERE participant_id='$current_number'");
$_SESSION['names'][] = $name;
foreach($_SESSION['names'] as $k=>$v){
echo '<li>'.$v.'</li>';
}
}
function check_existing($item, $array){
if(in_array($item, $array)){
return 1;
}else{
return 2;
}
}
Try mt_rand instead of rand. (http://php.net/manual/en/function.mt-rand.php)
Edit:
A gentleman in this post:
What is the best way to generate a random key within PHP?
suggested using
sha1(microtime(true).mt_rand(10000,90000))
According to PHP manual there is no need to seed the random number generator (at least since PHP 4.2.0). So it should return different number every time the php script is run. You can try to use also mt_rand(), but I don't think it will give you better randomness then rand(). The manual just says that it is 4x faster and that on some old libc versions the rand() was buggy - but I think that was written long time ago.
How many records do you need? You can just use SELECT * FROM table ORDER BY rand() LIMIT 5, however that can be slow if you have many large records in table (1000+).
If you have only 91 records it is quite possible that sometimes the generator will generate the same number again. For code on how to generate X unique random numbers, you can check answers to this question: Generating UNIQUE Random Numbers within a range - PHP
Related
I'm trying to get a random row from a table using RedBean. I tried this:
$amount = R::count($names);
$id = rand(0, $amount);
$randomname = R::load( $names, $id );
$name = $randomname["name"];
But soon i found that this way is not correct, because the table has gaps in the id numbers order, there can be ids 1-95, then 115-523, then 530-600, etc. There are gaps, because i deleted rows manually, so the random $id falls on these gaps sometimes and never falls on last rows, because the $amount is smaller than the last id.
As i understand after trying to find the answer in others' questions, the id should be left untouched, that means i don't need to try to change the ids themselves so that they won't contain gaps. So i'm trying to make something instead.
I've tried also this, it works, though i think it's too many checks for a single row result:
$randomnames = R::findAll($names);
$a = 0;
foreach($randomnames as $n){
if($a == $id){
$name = $n['name'];
$a = $amount;
}
else $a++;
}
Maybe i should use MySQL's RAND somehow, if there is nothing existing for this in RedBean itself, or R::exec with SQL... How is it the best to get a random row?
I've just started to learn both RedBean and PHP, tried to find an answer myself, also on RedBean's page, but couldn't find something specific for this. Also my first question here, so i hope i asked it correctly. :)
My two cents ;-)
$ids = R::getCol("select id from $names"); //returns an array of id's indexed as 0..number of names-1
$random_index = rand(0, sizeof($ids)-1);
$random_name = R::load( $names, $ids[$random_index] );
$name = $random_name["name"];
I am creating a project which involves getting some questions from mysql database. For instance, if I have 200 questions in my database, I want to randomly choose 20 questions in such a way that no one question will be repeated twice. That is, I want to be able to have an array of 20 different questions from the 200 I have every time the user tries to get the list of questions to answer. I will really appreciate your help.
SELECT * FROM questions ORDER BY RAND() LIMIT 20;
PS^ This method not possible for very big tables
Use Google to find a function to create an array with 20 unique numbers, with a minimum and a maximum. Use this array to prepare an SQL query such as:
expression IN (value1, value2, .... value_n);
More on the SQL here.
Possible array filling function here too.
Assuming you have contiguously number questions in your database, you just need a list of 20 random numbers. Also assuming you want the user to be able to take more than one test and get another 20 questions without duplicates then you could start with a randomised array of 200 numbers and select blocks of 20 sequentially from that set i.e.
$startQuestion=1;
$maxQuestion=200;
$numberlist= range(1,$maxQuestion);
shuffle($numberlist);
function getQuestionSet( $noOfQuestions )
{
global $numberlist, $maxQuestion, $startQuestion;
$start= $startQuestion;
if( ($startQuestion+$noOfQuestions) > $maxQuestion)
{
echo "shuffle...\n";
shuffle($numberlist);
$startQuestion=1;
}else
$startQuestion+= $noOfQuestions;
return array_slice($numberlist,$start,$noOfQuestions);
}
// debug...
for($i=0; $i<42; $i++)
{
$questionset= getQuestionSet( 20 );
foreach( $questionset as $num )
echo $num." ";
echo "\n";
}
then use $questionset to retrieve your questions
If you know how many rows there are in the table, you could do use LIMIT to your advantage. With limit you specify a random offset; syntax: LIMIT offset,count. Example:
<?php
$totalRows = 200; // get his value dynamically or whatever...
$limit = 2; // num of rows to select
$rand = mt_rand(0,$totalRows-$limit-1);
$query = 'SELECT * FROM `table` LIMIT '.$rand.','.$limit;
// execute query
?>
This should be safe for big tables, however it will select adjacent rows. You could then mix up the result set via array_rand or shuffle:
<?php
// ... continued
$resultSet = $pdoStm->fetchAll();
$randResultKeys = array_rand($resultSet,$limit); // using array_rand
shuffle($resultSet); // or using shuffle
?>
I need to generate a random number in the format 105-##### and I need to check this generated number against the already created numbers before saving it to the database to make sure it doesn't exist.
Here is my code to generate the number itself:
$digits = 5;
$second_num = str_pad(rand(0, pow(10, $digits)-1), $digits, '0', STR_PAD_LEFT);
$order_gen = "105-".$second_num;
Now that I have this $order_gen number, what is the best way to loop through what I already have in the order_number database table to make sure there are no duplicates? I was thinking of using a while loop but not sure how to apply it.
Given your algorithm, there isn't a better way except to query the database for each ID you generate, until you find one that isn't taken.
Wrap the entire thing in a do/while statement, and define a method which queries the database by that random ID, returning true if the record exists, and false otherwise:
do {
$digits = 5;
$second_num = str_pad(rand(0, pow(10, $digits)-1), $digits, '0', STR_PAD_LEFT);
$order_gen = "105-".$second_num;
} while (record_exists($order_gen);
Alternatively, generate randomly once, and then increment until you find a number that isn't taken:
$digits = 5;
$second_num = str_pad(rand(0, pow(10, $digits)-1), $digits, '0', STR_PAD_LEFT);
$order_gen = "105-".$second_num;
while (record_exists($order_gen) {
$second_num += 1
$second_num = str_pad($second_num, '0', STR_PAD_LEFT);
$order_gen = "105-".$second_num;
}
There are 3 options that I can think of:
Generate an id and query to see if it is used. If used, generate another and repeat until you find one that is not used. This will probably be the slowest.
Have a table with all possible ids. Select a random row from that table, use that id and delete it so it won't be used again. This requires a table just for holding these ids though. Depending on your needs, this might not be possible.
In PHP, query all existing ids order by id. Loop over result and make an array of all ids. From there you can generate, if exists in array generate again like #1 or make an array of all possible ids, array_diff($allIDS, $usedIDs) to find ones not in use and array_rand to get a random one. This option uses more memory in php having to query out all existing ids.
All 3 methods will possibly suffer from race conditions where an id could be duplicated if two requests happened at the same time. #2 would probably be easiest to prevent race conditions. On the delete query, check the count of affected rows (PDOStatement::rowCount if using PDO) and if 0, assume someone else got it before you could use it and get another id.
This can do the work
$rand = mt_rand(000123,999999); //prefix between 000123 to 999999
$id = "105".$rand;
$error = true;
while($error){
$query = "SELECT id FROM table WHERE id = '$id'";
$result = mysqli_query($conn,$query);
$count = mysqli_num_rows($result);
if($count!=0){
$error = true;
$rand = mt_rand(000123,999999);
$id = "105".$rand;
}else{
$error = false;
}
}
I'm trying to create a function that, when run, creates numerical ID, checks to see if that ID exists in the DB and if it does exists then create another ID and check again until it has a unique ID. I'm stuck on how to loop in the functions withing a function
function createUniqueID() {
function buildUnique() {
$uniqueID = rand(100000000000,999999999999);
return $uniqueID;
}
function compareWithDB($uniqueID) {
$s = "SELECT id FROM table WHERE id='{$uniqueID}'";
$r = mysql_query($s);
return $r;
}
function countDBRows($r) {
if(mysql_num_rows($r) >0){
$f = false; // found
} else{
$f = true;
}
}
$uniqueID = buildUnique();
$r = compareWithDB($uniqueID);
$f = countDBRows($r);
if (!$f) {
$uniqueID = 'nope';
}
return $uniqueID;
}
You're much better off to call MySQL's UUID() function, and store & return the value of that, unless the value absolutely must be numeric.
SELECT UUID();
If you want a unique number, then just use AUTO_INCREMENT
If you want a unique random number (why?) create a unique index on the ID column and keep trying until you get no errors. This is better for concurrency: multiple concurrent calls can have the same number and pass the test. Better still, let the DB engine do it with RAND...
Theres another way too,
You can use the sha1() function in conjuction with an static variable as shown:
function GetUniqueID() {
static $salt=1;
$id = sha1("somestring".(string)$salt);
$salt++;
return $id;
}
Here $salt being an static one retains its value between all calls incrementing ensuring unique ID's (hash of SHA1).for more security "somestring" can be made randomized also .
Might I suggest the much easier and more efficient uniqid function which will do this for you. It generates guaranteed unique IDs based on the timestamp in milliseconds. The generated Id is 13 digits long unless you decide to add a prefix or use extra 'entropy' (more uniqueness).
Edit: Note that this is both numbers and letters returned by the function.
http://php.net/manual/en/function.uniqid.php
Edit 2:
Using your method here is how to nest the loops...
while (true) {
$uniqueID = buildUnique();
$r = compareWithDB($uniqueID)
if (countDBRows($r)) { break; }
}
Your unique ID is then stored in $uniqueID. I do however discourage this because it's bulky and inefficient... but there you go! :)
I'm answering my own question because I have found a solution of creating a big number that (likely) will never duplicate. I use three random 2-digit variables and three different date syntax.
Putting them together makes it clear that they will never duplicate unless someone happens to post during the same year, week and with the same number of seconds in the minute an also have 3 different random number all be the same.
I think the chances of this happening would be in the millions and if there are millions of posts happening then i'm sure I will have more resources to solve this issue.
$rand1 = rand(10,99);
$rand2 = rand(10,99);
$rand3 = rand(10,99);
$date1 = date("s"); // seconds
$date2 = date("y"); // 2 digit year
$date3 = date("W"); // week number (out of 52)
$uniqueID = $date1.$rand1.$date2.$rand2.$date3.$rand3;
return $uniqueID;
I am trying to implement a check in my PHP code, that checks if there is a duplicate uid in the database, and if so, to assign a new uid, and check again, but I am having trouble nailing the logic, here is what I have thus far,
function check($uid){
$sql = mysql_query("SELECT * FROM table WHERE uid='$uid'");
$pre = mysql_num_rows($sql);
if($pre >= 1){
return false;
}else{
return true;
}
}
And then using that function I thought of using a while loop to continue looping through until it evaluates to true
$pre_check = check($uid);
while($pre_check == false){
//having trouble figuring out what should go here
}
So basically, once I have a usable uid, write everything to the database, else keep generating new ones and checking them till it finds one that is not already in use.
It is probably really simple, but for some reason I am having trouble with it.
Thanx in advance!
$uid = 100; // pick some other value you want to start with or have stored based on the last successful insert.
while($pre_check == false){
$pre_check = check(++$uid);
}
Of course ths is exactly what 'auto incrementing' primary keys are useful for. Are you aware of 'auto incrementing' primary keys in mysql?
EDIT
In light of your comment regarding maintaining someone else's code that uses the random function like that (ewwwww)... I would use the method I suggest above and store the last inserted id somewhere you can read it again for the next user. This will allow you to "fill-in-the-blanks" for the uids that are missing. So, if for example you have uids 1, 2, 5, 9, 40, 100... you can start with $uid = 1; Your while loop will return once you get to 3. Now you store the 3 and create the new record. Next time, you start with $uid = 3; and so on. Eventually you will have all numbers filled in.
It is also important to realize that you will need to do the inserts by either locking the tables for WRITES. You don't want to get into a race condition where two different users are given the same uid because they are both searching for an available uid at the same time.
Indeed the best is to use autoincrement ids, but if you don't have the choice, you can do a reccursive function like that:
function find_uid() {
$new_uid = rand(1000000000, 9999999999);
$sql = mysql_query("SELECT COUNT(*) AS 'nb' WHERE uid=".$new_uid.";");
$row = mysql_fetch_assoc();
$pre = $row['nb'];
return ($pre >= 1 ? find_uid() : $new_uid);
}
COUNT(*) should be more performant because the count is made by MySQL and not php.
By the way, if you need a new uid shouldn't the condition be ($pre > 0) instead of ($pre > 1) ?