I am trying to make a script to check if an int is already added to my database. If so, it will re-generate another random number and check again. If it doesn't exist, it'll insert into the database.
However, I am having troubles. If a number exists, it just prints out num exists, how would I re-loop it to check for another and then insert that? I have tried to use continue;, return true; and so on... Anyway, here is my code; hopefully someone can help me!
<?php
require_once("./inc/config.php");
$mynum = 1; // Note I am purposely setting this to one, so it will always turn true so the do {} while will be initiated.
echo "attempts: ---- ";
$check = $db->query("SELECT * FROM test WHERE num = $mynum")or die($db->error);
if($check->num_rows >= 1) {
do {
$newnum = rand(1, 5);
$newcheck = $db->query("SELECT * FROM test WHERE num = $newnum")or die($db->error);
if($newcheck->num_rows >= 1) {
echo $newnum . " exists! \n";
} else {
$db->query("INSERT test (num) VALUES ('$newnum')")or die($db->error);
echo "$newnum - CAN INSERT#!#!#";
break;
}
} while(0);
}
?>
I think the logic you're looking for is basically this:
do {
$i = get_random_int();
} while(int_exists($i));
insert_into_db($i);
(It often helps to come up with some functions names to simplify things and understand what's really going on.)
Now just replace the pseudo functions with your code:
do {
$i = rand(1, 5);
$newcheck = $db->query("SELECT * FROM test WHERE num = $i")or die($db->error);
if ($newcheck->num_rows >= 1) {
$int_exists = true;
} else {
$int_exists = false;
}
} while($int_exists);
$db->query("INSERT test (num) VALUES ('$i')") or die($db->error);
Of course, you can do a little more tweaking, by shortening...
// ...
if ($newcheck->num_rows >= 1) {
$int_exists = true;
} else {
$int_exists = false;
}
} while($int_exists);
...to:
// ...
$int_exists = $newcheck->num_rows >= 1;
} while($int_exists);
(The result of the >= comparison is boolean, and as you can see, you can assign this value to a variable, too, which saves you 4 lines of code.)
Also, if you want to get further ahead, try to replace your database calls with actual, meaningful functions as I did in my first example.
This way, your code will become more readable, compact and reusable. And most important of all, this way you learn more about programming.
The logic is incorrect here. Your do-while loop will get executed only once (as it's an exit-controlled loop) and will stop on the next iteration as the while(0) condition is FALSE.
Try the following instead:
while($check->num_rows >= 1) {
$newnum = rand(1, 5);
$newcheck = $db->query("SELECT * FROM test WHERE num = $newnum")or die($db->error);
if ($newcheck->num_rows >= 1) {
echo $newnum . " exists! \n";
} else {
$db->query("INSERT test (num) VALUES ('$newnum')") or die($db->error);
echo "$newnum - CAN ISNERT#!#!#";
break;
}
}
Sidenote: As it currently stands, your query is vulnerable to SQL injection and could produce unexpected results. You should always escape user inputs. Have a look at this StackOverflow thread to learn how to prevent SQL injection.
Here is an example of some code that I threw together using some of my previously made scripts. You will notice a few changes compared to your code, but the concept should work just the same. Hope it helps.
In my example I would be pulling the database HOST,USER,PASSWORD and NAME from my included config file
require_once("./inc/config.php");
echo "attempts: ---- ";
$running = true;
while($running == true) {
//create random number from 1-5
$newnum = rand(1,5);
//connect to database
$mysqli = new mysqli(HOST, USER, PASSWORD, NAME);
//define our query
$sql = "SELECT * FROM `test` WHERE `num` = '".$$newnum."'";
//run our query
$check_res = mysqli_query($mysqli, $sql) or die(mysqli_error($mysqli));
//check results, if num_rows >= our number exists
if (mysqli_num_rows($check_res) >= 1){
echo $newnum . " exists! \n";
}
else { //our number does not yet exists in database
$sql = "INSERT INTO `test`(`num`) VALUES ('".$newnum."')";
$check_res = mysqli_query($mysqli, $sql) or die(mysqli_error($mysqli));
if ($check_res){
echo $newnum . " - CAN ISNERT#!#!#";
// close connection to datbase
mysqli_close($mysqli);
}
else{
echo "failed to enter into database";
// close connection to database
mysqli_close($mysqli);
}
break;
}
}
I would also like to note that this will continue to run if all the numbers have been used, you may want to put in something to track when all numbers have been used, and cause a break to jump out of the loop.
Hope this helps!
Related
I need to check if a value exists in my database
I have a table where every user has an unique code. For example: 5h27f.
These values and users add up very quickly. So very soon I might have +2000 unique codes. What's the best, fastest and most efficient way to check if a value is unique?
foreach ($users as $user) {
$is_unique = FALSE;
while ($is_unique == FALSE) {
$code = unique_code();
$query = "SELECT * FROM unique_code_table WHERE code='$code';";
$res = $mysqli->query($query);
if ($res->num_rows > 0 {
} else {
$is_unique = TRUE;
}
}
}
OR
$query = "SELECT code FROM unique_code_table;";
$res = mysqli->query($query);
$codes = array();
$i = 0;
while ($row = $res->fetch_object()) {
$codes[$i] = $row->code;
$i++;
}
$code = unique_code();
while (in_array($code, $codes) {
$code = unique_code();
}
(this code might not be 100% accurate, I've written this just to explain the purpose of the question.)
I'd say that one query trip to the database vs. potentially 2000+ is significantly better to do. Second script will be significantly faster.
On the first code a LIMIT 1 would do wonders but compared to the second query it will pale as far as benchmarks are concerned.
Put the following at the bottom of your script to fine tune and benchmark:
PHP 5.4 +
$sParseTime = microtime(true) - $_SERVER["REQUEST_TIME_FLOAT"];
echo $sParseTime;
<?php
$query = $_GET['query'];
// gets value sent over search form
$min_length = 6;
// you can set minimum length of the query if you want
if(strlen($query) >= $min_length){ // if query length is more or equal minimum length then
$query = htmlspecialchars($query);
// changes characters used in html to their equivalents, for example: < to >
$query = mysql_real_escape_string($query);
// makes sure nobody uses SQL injection
$raw_results = mysql_query("SELECT * FROM cwnational WHERE (`postcode` = '$query') OR (`structure` LIKE '%".$query."%')") or die(mysql_error());
if(mysql_num_rows($raw_results) > 0){ // if one or more rows are returned do following
while($results = mysql_fetch_array($raw_results)){
echo "<p><h3>".$results['postcode']."</h3>".$results['structure']."</p>";
}
while($results = mysql_fetch_array($raw_results)){
if($results['structure'] = "National") {
echo "print national_table";
} else {
echo "print local_table";
}
}
else{ // if there is no matching rows do following
echo "No results";
}
}
else{ // if query length is less than minimum
echo "Minimum length is ".$min_length;
}
?>
</body>
I'm totally stumped now..
When I successfully match a $query, I want to use the 2nd part of the array which should be a column called structure and use that as a switch to either print table_local or table_national from the db.
re-wrote it after getting to grips with the correct approach,
if (empty($searchTerm)) {
echo "<h1>Empty search term</h1>";
$sqlQuery = false;
} elseif (strlen($searchTerm) < $minQueryLength) {
echo "<h1>Search term must be at least ".$minQueryLength." characters long.";
$sqlQuery = false;
} else {
if (strlen($firstPart) + strlen($secondPart) == 7) {
$sqlQuery = $mysqli->query("SELECT `postcode`, `structure` FROM `cwnational` WHERE `postcode` LIKE '".$firstPart." ".$secondPart."'");
} else {
$sqlQuery = $mysqli->query("SELECT `postcode`, `structure` FROM `cwnational` WHERE REPLACE(`postcode`, ' ', '') LIKE '".$searchTerm."%'");
}
}
if (is_object($sqlQuery) && $sqlQuery->num_rows >= 1) {
$resultArr = array();
while($row = $sqlQuery->fetch_assoc()) {
$resultArr[$row['postcode']] = array();
$priceQuery = $mysqli->query("SELECT `base_rate`, `commit_mbps` FROM `pricing` WHERE `structure` = '".$row['structure']."'");
if ($priceQuery->num_rows >= 1) {
while ($price = $priceQuery->fetch_assoc()) {
$resultArr[$row['postcode']][$price['commit_mbps']] = ((float)$price['base_rate'] + $transit[$price['commit_mbps']] ) + ( ( $transit[$price['commit_mbps']] + (float)$price['base_rate'] ) * ((float)$apiUser['margin']/100) ) ;
}
}
}
You're reading the result set twice. This will work for the first loop, but won't execute the second because you've already reached the end. If you need to read the results twice, use this:
mysql_data_seek ($raw_results , 0 )
immediately before the second loop to set the pointer to the beginning again.
Of course, you should be using mysqli...
what's going wrong now? I see you need to add an equals sign here:
if($results['structure'] == "National") {
note: double equals for php conditional
It also looks like you have an "else" coming out of a "while", that won't work. And you did this "while" loop twice, I'd combine them into a single "While":
while($results = mysql_fetch_array($raw_results)){
I'm a little confused about breaking out and continuing out of loops etc. I have 2 SQL queries that match user priveleges against the user's actual priveleges with the new ones put it. However, if some of the new priveleges match the one the user has, I want to skip the SQL insert and move on to the next one:
public static function insertPriveleges($user_id,$priveleges)
{
$ex = explode(",",$priveleges); // separated by commas
if(count($ex)>0)
{
$x = false;
foreach($ex as $i => $priv)
{
$check_user = mysql_query("SELECT * FROM users_access_codes WHERE user_id='$user_id'") or die(mysql_error()); // get user's current priveleges
while($check_data = mysql_fetch_array($check_user))
{
if($check_data['access_code']!=$priv)
{
//if it doesn't match, insert
$sql = "INSERT INTO users_access_codes (uaID,user_id,access_code) VALUES (NULL,'".$user_id."','$priv')";
}
}
}
}
}
I almost never have a situation that needs to match more than two things in loops. I need to make sure I don't end up with double priveleges for that user. I know there must be a 'continue' statement somewhere in the inner loop, but not sure where.
After your INSERT statement, you can add continue 2 to bring you back to the top of your foreach ($ex as .... You can also use break; in this case because there's nothing after your inner while.
However, you don't actually need it if you do it differently. Instead of reading the table for each privilege, just read all of them once and them compare.
This code will get all privileges from the database and then only inserts those that are missing, based on $ex; it uses array_diff() to calculate the difference between the two.
public static function insertPriveleges($user_id, $priveleges)
{
$ex = explode(",", $priveleges); // separated by commas
if (count($ex) > 0) {
// get user's current priveleges
$check_user = mysql_query("SELECT * FROM users_access_codes
WHERE user_id='$user_id'") or die(mysql_error());
$actual = array();
while ($row = mysql_fetch_array($check_user)) {
$actual[] = $row['access_code'];
}
foreach (array_diff($ex, $actual) as $priv) {
//if it doesn't match, insert
$sql = "INSERT INTO users_access_codes (uaID,user_id,access_code) VALUES (NULL,'".$user_id."','$priv')";
mysql_query($sql);
}
}
}
Btw, you could consider using INSERT IGNORE INTO because of race conditions, but because you're not checking the statement return value, it won't matter here :)
Simply add a break after the INSERT:
public static function insertPriveleges($user_id,$priveleges)
{
$ex = explode(",",$priveleges); // separated by commas
if(count($ex)>0)
{
$x = false;
foreach($ex as $i => $priv)
{
$check_user = mysql_query("SELECT * FROM users_access_codes WHERE user_id='$user_id'") or die(mysql_error()); // get user's current priveleges
while($check_data = mysql_fetch_array($check_user))
{
if($check_data['access_code']!=$priv)
{
//if it doesn't match, insert
$sql = "INSERT INTO users_access_codes (uaID,user_id,access_code) VALUES (NULL,'".$user_id."','$priv')";
break;
}
}
}
}
}
To be complete I would recommand the reading of the following link:
http://php.net/manual/en/faq.databases.php#faq.databases.mysql.deprecated
Using a example from elsewhere on SO to better catch 'hiding' errors. While the code below will catch and return an error, is it possible to improve this to report for which query the error occurred?
With the code below, the output is:
Columns: 18
Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'FRO inventory' at line 1
Code being tested:
$query = "SELECT * FROM orders WHERE location = 'IN' ORDER BY orderNum DESC LIMIT 20;";
$query .= "SELECT * FRO inventory"; // With error
$ord = array();
$invent = array();
if(mysqli_multi_query($link, $query)) {
do {
// fetch results
if($result = mysqli_store_result($link)) {
echo 'Columns: ' . mysqli_field_count($link) . "<br>";
while($row = mysqli_fetch_assoc($result)) {
if(count($row) > 17)
$orders[] = $row;
elseif(count($row) == 6)
$inv[] = $row;
}
}
if(!mysqli_more_results($link))
break;
if(!mysqli_next_result($link)) {
// report error
echo 'Error: ' . mysqli_error($link);
break;
}
} while(true);
mysqli_free_result($result);
}
Here is an approach that will not only improve the quality of your error messages, it will improve the way you handle your result sets.
$q["Orders"] = "SELECT * FROM orders WHERE location = 'IN' ORDER BY orderNum DESC LIMIT 20";
$q["Inventory"] = "SELECT * FRO inventory";
if (!$link = mysqli_connect("host", "user", "pass", "db")) {
echo "Failed to connect to MySQL: " , mysqli_connect_error();
} elseif (mysqli_multi_query($link, implode(';', $q))) {
do {
$q_key = key($q); // current query's key name (Orders or Inventory)
if ($result = mysqli_store_result($link)) { // if a result set... SELECTs do
while ($row = mysqli_fetch_assoc($result)) { // if one or more rows, iterate all
$rows[$q_key][] = $row;
}
mysqli_free_result($result);
echo "<div><pre>" . var_export($rows[$q_key], true) . "</pre></div>";
}
} while (next($q) && mysqli_more_results($link) && mysqli_next_result($link));
}
if ($mysqli_error = mysqli_error($link)) { // check & declare variable in same step to avoid duplicate func call
echo "<div style=\"color:red;\">Query Key = " , key($q) , ", Query = " , current($q) , ", Syntax Error = $mysqli_error</div>";
}
Error on first query:
If your first query tries to access a table that doesn't exist in the nominated database like: ordersXYZ
Array $rows will not exist, no var_export() will occur, and you will see this response:
Query Key = Orders, Query = SELECT * FROM ordersXYZ WHERE location='IN' ORDER BY orderNum DESC LIMIT 20, Syntax Error = Table '[someDB].ordersXYZ' doesn't exist
Error on second query:
If your first query is successful, but your second query tries to access a non-existent table like: inventory2
$rows["Orders"] will hold the desired row data and will be var_export()'ed, $row["Inventory"] will not exist, and you will see this response:
Query Key = Inventory, Query = SELECT * FROM inventory2, Syntax Error = Table '[someDB].inventory2' doesn't exist
No errors:
If both queries are error free, your $rows array will be filled with the desired data and var_export()'ed, and there will be no error response. With the queried data saved in $rows, you can access what you want from $rows["Orders"] and $rows["Inventory"].
Things to note:
You may notice that I am making variable declarations and conditional checks at the same time, this makes the code more concise but some devs prefer to avoid this.
As my approach uses implode() with a semi-colon on the elseif line, be sure not to add a trailing semi-colon to your queries.
This set of queries always returns a result set because all are SELECT queries, if you have a mixed collection of queries that affect_rows, you may find some useful information at this link(https://stackoverflow.com/a/22469722/2943403).
mysqli_multi_query() will stop running queries as soon as there is an error. If you are expecting to catch "all" errors, you will discover that there will never be more than one.
Writing conditional break points like in the OP's question and solution is not advisable. While custom break points may be rightly used in other circumstances, for this case the break points should be positioned inside of the while() statement of the do() block.
A query that returns zero rows will not cause a error message -- it just won't create any subarrays in $rows because the while() loop will not be entered.
By using the key() function, the OP's if/elseif condition that counts the columns in each resultset row can be avoided. This is better practice because running a condition on every iteration can become expensive in some cases. Notice that the array pointer is advanced inside of $q at the end of each do() iteration. This is an additional technique that you will not find on the php manual page; it allows key() to work as intended.
And, of course, the <div><pre>var_export()...</pre></div> line can be removed from your working code -- it was purely for demonstration.
If you are going to run any more queries after this code block that reuse variables, be sure to clear all used variables so that residual data does not interfere. e.g. $mysqli_error=null; // clear errors & reset($q); // reset array pointer.
Take heed to this somewhat vague warning at your own discretion: http://php.net/manual/en/mysqli.use-result.php :
One should not use mysqli_use_result() if a lot of processing on the
client side is performed, since this will tie up the server and
prevent other threads from updating any tables from which the data is
being fetched.
Lastly and MOST IMPORTANTLY for security reasons, do not display query or query error information publicly -- you don't want sinister people to see this kind of feedback. Equally important, always protect your queries from injection hacks. If your queries include user-provided data, you need to filter/sanitize the data to death before using it in mysqli_multi_query(). In fact when dealing with user input, my very strong recommendation is to move away from mysqli_multi_query() and use either mysqli or pdo prepared statements for your database interactions for a higher level of security.
To answer my own question and since the documentation is poor, here's a solution that hopefully will help others. What was missing is a way to catch an error on the 1st query. (The hidden actions of myqsqli_multi_query are difficult to understand.)
Now check for entries in $err array.
$q[1] = "SELECT * FROM orders WHERE location = 'IN' ORDER BY orderNum DESC LIMIT 20";
$q[2] = "SELECT * FROM inventory";
$ord = array();
$invent = array();
$err = array();
$c = 1;
if(mysqli_multi_query($link, implode(';', $q))) {
do {
// fetch results
if($result = mysqli_use_result($link))
while($row = mysqli_fetch_array($result, MYSQLI_ASSOC)) {
if(count($row) > 17)
$orders[] = $row;
elseif(count($row) == 6)
$inv[] = $row;
}
}
$c++;
if(!mysqli_more_results($link))
break;
if(!mysqli_next_result($link) || mysqli_errno($link)) {
// report error
$err[$c] = mysqli_error($link);
break;
}
} while(true);
mysqli_free_result($result);
}
else
$err[$c] = mysqli_error($link);
mysqli_close($link);
In your do loop, add a counter, each successful mysqli_next_result increment the counter. Once mysqli_next_result returns false, output the counter as well.
This works for two queries.
If the error is in the first, the response for 1st query to PHP is the error message, and for the second (which won't fire), a message tattling on the first.
If the error is in the second, the first's response is returned and the 2nd gets the error message.
I'm using associative arrays and nixing array elements[0].
This adds ['Error'] key only if there is a relevant error.
Finally, I'm not the best PHPer, so it's up to you to fix what's ugly.
$query_nbr=0;
if (mysqli_multi_query($link,$query ))
{
do
{
unset($field_info) ; $field_info = array() ;
unset($field_names) ; $field_names = array() ;
unset($values) ; $values = array(array()) ;
if ($result = mysqli_store_result($link))
{
$query_nbr += 1 ;
$rows_found = mysqli_num_rows($result);
$fields_returned = mysqli_num_fields($result);
$field_info = mysqli_fetch_fields($result);
$field_nbr=0;
foreach ($field_info as $fieldstuff)
{ $field_nbr +=1 ;
$field_names[$field_nbr] = $fieldstuff->name ;
}
$now = date("D M j G:i:s T Y") ;
if ($query_nbr == 1)
{
$result_vector1 = array('when'=>$now) ;
$result_vector1['nrows']=0;
$result_vector1['nrows']=$rows_found ;
$result_vector1['nfields']=$fields_returned ;
$result_vector1['field_names']=$field_names ;
}
else
{
$result_vector2 = array('when2'=>$now) ;
$result_vector2['nrows2']=0;
$result_vector2['nrows2']=$rows_found ;
$result_vector2['nfields2']=$fields_returned ;
$result_vector2['field_names2']=$field_names ;
}
$row_nbr=0 ;
while ($row = mysqli_fetch_array($result, MYSQLI_BOTH))
{
$row_nbr++ ;
for ($field_nbr=1;$field_nbr<=$fields_returned;$field_nbr++)
{
$values[$row_nbr][$field_names[$field_nbr]] =$row[$field_nbr-1] ;
}
}
mysqli_free_result($result) ;
unset($values[0]) ;
if ($query_nbr == 1)
{$result_vector1['values']=$values ;}
else
{$result_vector2['values2']=$values ;}
} // EO if ($result = mysqli_store_result($link))
} while (mysqli_more_results($link) && mysqli_next_result($link)) ;
} // EO if (mysqli_multi_query($link,$query ))
else
{
// This will be true if the 1st query failed
if ($query_nbr == 0)
{
$result_vector1['Error'] = "MySQL Error #: ".mysqli_errno($link).": ".mysqli_error($link) ;
$result_vector2['Error'] = "MySQL Error in first query." ;
}
} // EO MySQL
// Here we only made it through once, on the 2nd query
if ( $query_nbr == 1 && $nqueries == 2 && empty( $result_vector2 ) )
{
$result_vector2['Error'] = "MySQL Error #: ".mysqli_errno($link).": ".mysqli_error($link) ;
}
I need to use the goto operator in my code as I can't seem to think of a way around it. However the problem is my host only has PHP version 5.2.17 installed.
Any ideas?
Below is my code:
if ($ready !=="y")
{
$check=mysql_query("SELECT `inserted` FROM `team`");
$numrows = mysql_num_rows($check);
$i="0";
while ($i<$numrows && $row = mysql_fetch_assoc($check))
{
$array[$i] = $row['inserted'];
$i++;
}
if (in_array("n", $array))
{
goto skip;
}
else
{
mysql_query("
UPDATE game SET ready='y'
");
}
}
skip:
There are a few anti-patterns in your code. Let's clean it up. I'll explain what's been changed in a jiffy.
if($ready !== "y") {
$sth = mysql_query("SELECT inserted FROM team WHERE inserted = 'n'");
if(mysql_num_rows($sth) > 0) {
mysql_query("UPDATE game SET ready = 'y'");
}
}
First things first: There is no need to perform a query, fetch all of the results, then loop through those results (in_array) looking for a specific value. Let the database do that for you by expressly looking only for rows where inserted is the string literal "n".
Because we know that we're only getting "n" records back, we just need to check if there are any results. If so, run the query. If there are no "n" records, the UPDATE isn't run.
If you need to know that the UPDATE ran, add a check for it:
$ran_update = false;
if($ready !== "y") {
$sth = mysql_query("SELECT inserted FROM team WHERE inserted = 'n'");
if(mysql_num_rows($sth) > 0) {
mysql_query("UPDATE game SET ready = 'y'");
$ran_update = true;
}
}
if($ran_update) {
// ...
}
You want to use the correct control word to break from the loop:
if ($ready !=="y")
{
$check=mysql_query("SELECT `inserted` FROM `team`");
$numrows = mysql_num_rows($check);
$i="0";
while ($i<$numrows && $row = mysql_fetch_assoc($check))
{
$array[$i] = $row['inserted'];
$i++;
}
if (in_array("n", $array))
{
break;
}
else
{
mysql_query("
UPDATE game SET ready='y'
");
}
}
The break keyword will do exactly what you want: End the execution of the while loop. Never ever ever use a goto!
As a direct response to the title of this post:
do{
if (!$condition){
break;
}
// Code called if conditions above are met
}while(0);
//Code always called
In some circumstances, this, or a goto, can make for very tidy and readable code.
First, you could use a break to exit your initial loop. Second, if you need to test for anything, set a variable (must be global not local) as a flag or indicator before calling break, then do a conditional test statement where your skip line is to perform any additional steps you need.